typescript
命令
tsc --init 生成配置文件
{'compilerOptions': {
'target': 'es5',
'module': 'commonjs',
'strictNullChecks': true // 是否严格null检查,为true,undefined和null不为基础数据类型的子集
}}
scripts:{
'build': 'tsc'
'start': 'tsc--watch'
}
ts的基础数据类型
特殊数据类型
数组
1.长度任意
2.类型必须统一
let names:string[] = ['a', 'b', 'c']
let names2: Array<string> = ['a', 'b', 'c'] // 泛型写法
元组
1.长度和类型都确定的数组
let persong:[string, number, string] = ['zhufeng', 10, 'male']
console.log(person[0].length)
console.log(person[1].tofixed(2))
console.log(person[2].length)
枚举类型
enum Gender {
BOY,
GIRL
}
let boy: number = Gende.BOY // 0
常量枚举
const enum Colors {
RED,
YELLOW,
BULE
}
let colors= [Colors.RED, Colors.YELLOW, Colors.BULE] // [0,1,2]
Any 任意类型
let x:any
never 类型
1.作为没有返回值的函数的返回值类型
function get():never {
console.log(1)
throw new Error('报错了')
}
never和void的区别
-
void可以被赋值为null和undefined的类型。never则是一个不包含值的类型
-
拥有void返回值类型的函数能正常运行。拥有never返回值类型的函数无法正常返回,无法中止,或者抛出异常
包装类
基本数据类型是没有方法的
let name1 = 'zhufeng'
// -----------------------------
new String('zhufeng').toLocleLowweCase()
// -----------------------------
let hasGirl:boolean = true
hasGirl = Boolean(true)
hasGirl = new Boolean(true) // 这里是错误的,基本数据类型内置已经帮我们转了对象,所以不能用对象进行复制
当你在一个基本数据类型上调用方法时,会立刻隐含的把一个基本数据类型包装成对象
联合类型
let name: string | number
name = 'zhufeng'
name = 10
类型断言
let name: string | number
name = 'zhufeng'
name = 10
(name as tring).length
function getName(val: string|number|boolean) {
(val as string).length
}
type
// type 用来定义类型别名
// 函数表达式
type GetUserName = (x:string, y:string) => string
let getUserName: GetUserName = function(firstName, lastName):string {
return firstname+lastname
}
// 可选参数 加上问号
let items:number[] = [1,2,3,4]
type IteratorFun = (item:number, index?:number, arr?:number[]) => void
let iteratorFun:IteratorFun = function(item:number, index:number, arr:number[]) {}
item.forEach(iteratorFun)
// 默认参数
function ajax(url:string, methods:string="GET"){
}
ajax('/user')
// 剩余参数
function sum(prexfix: string, ...args:number[]) {
return prefix+arg.reduce((val, item) => val+=item,0)
}
let r = sum('$', 1,2,3,4)
// 类型的重载
type MyType = string|number|boolean
function getType(val:MyType):MTtype {
return val
}
// 函数的重载
function double(val:string): string
function double(val:number): number
function double(val:boolean):boolean
function double(val:any) {
if (typeof val === 'string') {
return val+val
}
if (typeof val === 'number') {
return val*2
}
if(typeof val=== 'boolean') {
return val
}
}
类
// get name
class Person1 {
myname: string,
constructor(myname:string){
this.myname = myname
}
get name() {
return this.myname
}
set name(val) {
this.myname = val
}
}
let p1 = new Person1()
p1.name
参数的属性
// 加上public 之后相当于给当前的实例增加一个公有属性
// public priviate protected
class Person2 {
constructor(public myname:string){
}
}
// readonly 只读 参数不可改
class Person4{
public readnoly id:number
constructor(id:number) {
this.id = id
}
}
class Parent {
public name:string; // 公有的name属性,所有地方都能访问
protected age:number; // 受有限度的保护的属性,只能在自己或自己的子类中被访问
priviate money:number; // 私有属性,只能自己访问,连自己的子类都不能访问
constructor(name:string, age:number, money:number) {
this.name = name
this.age = age
this.money = money
}
getName() {
console.log('a', this.name)
}
getAge() {
console.log('a', this.age)
}
getMoney() {
console.log('a', this.money)
}
}
class Child extends Parent {
getName() {
console.log('b', this.name)
}
getAge() {
console.log('b', this.age)
}
getMoney() {
console.log('b', this.money)
} // 在这里已经报错不能访问了
}
let c = new Child('zfpx',10,100)
c.getName() // 可以正常访问
c.getAge() // 只能在子类中访问,这里是实例
c.getMoney() // 只能在自己中访问
静态属性
// 普通类
class Father {
static myname:string = 'zhufeng'
static getMyName() {
return Father.myname
}
}
console.log(Father.name)
Father.getMyName()
// 抽象类 abstract
// 抽象类是一种抽象的概念,无法被实例化,只能被继承
// 抽象类里的方法是抽象的
// 重写 父类是一样 子类重写父类逻辑 子类重写竭诚父类中的方法
// 重载 同一个函数提供多个类型定义
abstract class Animal {
name:string;
abstract speak()
}
class Cat extends Animal{
speak() {
// 要把抽象类的方法执行了才不会报错
}
}
继承vs多态
- 继承 子类继承父类,子类除了拥有父类所有的特性外,还有一些更具体的特性
- 多态 由继承而产生了相关不同的类,对同一个方法可以有不同的响应
接口
- 用来描述一种对象解构或者对象的形状
- 用来描述一种抽象的特性集合
interface UserInterface{
name:string
age:number
}
let user:UserInterface= {
name:'zhufeng',
age:10
}
// 抽象特性集合
interface Flyable{
fly():void
}
class Bird implements Flyable{
fly(){
console.log('小鸟飞')
}
}
interface Person6 {
readonly id: number;
name: string
[propName:string]:any // [propName] key 表示未知的其它属性
}
let p3: Person = {
id: 1,
name: 'zhufeng'
}
// 接口也可以继承
interface speakable2{
speak():void
}
interface speakChinese{
speakChinese():void
}
class chinesePerson implements speakChinese {
// 要同时实现接口及继承接口的方法
speakChinese() {}
speak(){}
}
// 函数类型接口
// 接口还可以用来约束函数
interface Discount {
(price:number):number
}
function discount(price:number):number{
reuturn price*.8
}
let d:Discount = discount(10)
// 可索引接口
// 可以对对象和数组进行约束
interface UserInterface1 {
[index:number]:string
}
let user1:UserInterface1 = ['a', 'b', 'c'] // 索引是数字
interface UserInterface2 {
[index:string]:string
}
let user2:UserInterface2 = {name: 'zhufeng'} //
泛型
在定义函数、接口或类的时候,不预先指定具体的类型,而是在使用的时候再制定类型的一种特性
// T相当于占位符,在函数使用的时候才传进来
function createArray<T>(length:number,val:T):t[] {
let arr:T[] = []
for (let i = 0;l<length;i++) {
arr[i] = val
}
return arr
}
createArray<string>(3,'x')
类数组
function sum(...args:number[]) {
let a2:IArguments = arguments // arguments就是类数组
}
sum(1,2,3)
// IArguments 相当于一个接口
interface IArguments {
[index: number]:any;
length:number;
callee: Function
}
let root = document.getElementById('root')
let children:HTMLCollection = root.children
let childNodes:NodeListOf<ChildNode> = root.childNodes
泛型类
在类中使用泛型
T = Type
class MyArray<T>{
list:T[] = []
add(val:T) {
this.list.push(val)
}
getFirst():T {
return this.list[0]
}
}
let myArray = new MyArray<number>() // 使用时不写泛型也不会报错,会进行类型推论
myArray.add(1);myArray.add(2)
let f = myArray.getFirst() // 1
// 泛型接口 就是在定义接口的时候使用泛型
interface SUM<T> {
(a:T,b:T):T
}
let sum3:SUM<number> = function(a:number, b:number):number{
return a+b
}
let r2 = sum3(1,2)
// 泛型可以有多个
// tuple 长度和类型都确定的数组
function swap<A,B>(tuple:[A,B]):[B,A] {
return [tuple[1], tuple[0]]
}
swap<string,number>(['a',1]) // [1,'a']
// 默认泛型类型 T=number 默认值
class MyArray<T=number>{
list:T[] = []
add(val:T) {
this.list.push(val)
}
getFirst():T {
return this.list[0]
}
}
let myArray = new MyArray<string>() // 使用时不写泛型也不会报错,会进行类型推论
myArray.add(1);myArray.add(2)
let f = myArray.getFirst() // 1
// 泛型的约束
// 在函数中使用泛型的时候,由于预先不知道泛型的类型,所以不能随意访问相应类型的属性和方法
function logger<T>(val:T) {
console.log(val.length)// 这里不知道val的参数类型,这样写会报错
}
interface LengthWise{
length: number
}
// T 要实现LengthWise这个接口,接口中定义了length的类型
function logger<T extends LengthWise>(val:T) {
console.log(val.length)
}
// 泛型类型的别名
type Cart<T> = {list:T[]}|T[]
let cart:Cart<string> = ['a','b','c']
let cart2:Cart<string> = {list: ['a','b','c']}
// 泛型接口vs泛型类型别名
// 接口会创建一个新的名字,它可以在任意地方被调用。而类型别名并不是创建新的名字,例如报错信息就不会使用别名
// 类型别名不能被extends 和 implements,这时我们应该尽量使用接口代替类型别名
// 当我们需要使用联合类型或元组类型的时候,类型别名会更合适
解构类型系统
- 接口的兼容性
- 如果传入的变量和声明的类型不匹配,TS就会进行兼容性检查
interface Animal {
name: string;
age:number;
gender:number
}
let a:Animal={
name: 'zhufeng';
age:10,
gender:0
}
interface Person {
name:string;
age:number
}
function getName(p:Person):string {
return p.name
}
getName(a)
// getName 的参数p:Person是person类型的接口,a是animal的接口,跟person接口相比,属性多了一个gender属性
// 在检查参数类型的时候,并不是比较接口类型,而是比较具体的属性是否兼容,对应的属性只能多,不能少
// 基本数据类型的兼容性
let num:string|number;
let str:string;
num = str
// str2 赋值给num2 是因为num2 有toString方法,是个字符串,字符串可以给字符串赋值
let num2:{
toString():string
}
let str2:string
num2 = str2
// 类的兼容性
// 父子之间能不能赋值,跟是不是父类子类没有关系,关键就看要的属性有没有
class Perent{
name:string
}
class child {
age:number
}
let p:Parent = new Person()
let c:Child = new Child()
// 将一个子类的实例赋给父类的变量
let pp:Parent = new Child()
// 将一个父类的变量赋给一个子类的变量
let cc:Child = new Parent()
// 函数的兼容性
// 比较函数的时候,先比较函数上的参数,在比较函数上的返回值
// 参数是可以省略的
type SumFunc = (a:number,b:number)=>number
let sum:SumFunc;
sum = function(a:number,b:number):number{
return a + b
}
sum2 = functon (a:number):number{
return a
}
// 参数可以少不能多
sum3 = functon ():number{
return 0
}
// 这里可不是箭头函数,箭头左边是参数列表, 右边是返回值,是一个对象,声明的是属性而不是值
// 返回值可以多,不能少,不然调用属性的时候会报错
type GetPerson = () => {name: string,age:number}
let getPerson:GetPerson = function() {
return {name: 'zhufeng',age:10}
}
// 函数参数的双向协变
// 函数的参数中目标兼容源,或者源兼容目标都可以,只有一个成立就可以
type LogFunc= (val:number|string)=>void
// LogFunc 兼容下面的 log1
let log1:LogFunc;
log1 = function(val:number) {console.log(val)}
// log2 兼容上面的LogFunc
log2 = function(val:number|string|boolean) {
console.log(val)
}
// 泛型的兼容性
// 泛型在判断兼容性的时候,会先判断具体的类型,再进行兼容性的判断
interface Empty<T> {}
let x1:Empty<string>
let y1:Empty<number>
x1 = y1
interface NotEmpty<T> {data:T}
let x2:NotEmpty<string>={data: '123'}
let y2:NotEmpty<number>={data:123}
x2 = y2
// 枚举的兼容性
// 枚举和数字兼容 比较值
enum Colors {
Red, // 0
Yellow, // 1
Blue // 2
}
// 相当于
// let Colors ={ '1': Red,'2': Yellow; '3': Blue}
let num:number = Colors.Red // 0
let c:Colors;
c = Colors.Red
c = 1
// 数字可以赋值给枚举变量,枚举变量也可以赋值给数字
类型保护
类型保护就是一些表达式,他们在编译的时候就能通过类型信息确保某个作用域内变量的类型
类型保护就是能通过关键字判断出分支中的类型
// 分支中判断类型
function double(input: string|number|boolean) {
if (typeof input === 'number') {
return input*2
}
if (typeof input === 'string') {
return input+input
}
if (typeof input === 'string') {
return !input
}
}
class Bird{
name1: string
}
class Dog {
name2: string
}
function getName(animal:Bird|Dog) {
if (animal instanceof Bird) {
return animal.name1
}
if (animal instanceof Dog) {
return animal.name2
}
}
// null保护
// 在默认情况下 null的检查是不严格的
function getFirstLetter (str: string|null) {
// 第一种处理方式
str = str || ''
return str.charAt(0)
}
// 链判断运算符 ES2020
// 是一种先检查属性是否存在你,再尝试访问改属性的运算符,符号位?
// 原理就是三元运算符
let a:any={
b:'zhufeng'
}
a = null
console.log(a?.b) // 'zhufeng'
console.log(a == null?undefined:a.b)
// 可辨识的联合类型
// 就是利用联合类型中的共有字段进行类型保护的一种技巧
// 相同字段的不同取值就是可辨识
// 在联合属性中,通过相同的值来判断 class
interface WarningButton {
class:'warning',
text1: '修改'
}
interface DanagerButton {
class:'danager',
text2: '删除'
}
type Button = WarningButton | DangerButton
function getButton(button:Button) {
if (button.class == 'warning') {
console.log(button.text1)
}
if (button.class == 'danger') {
console.log(button.text2 )
}
}
interface Bird {
swing:number
}
interface Dog {
leg: number
}
// 判断属性有没有
function getNumber(animal:Bird|Dog) {
if ('swing' in animal) {
console.log(animal.swing)
}
if ('leg' in animal) {
console.log(animal.leg)
}
}
// 以dog bird为例-----------------------------
// 自定义类型检查函数 x is Bird类型谓词
function isBird (x:Bird|Dog):x is Bird {
return (x as Bird).swing>0
return (<Bird>x).swing>0 // 使用断言,只能用Bird或者Dog
}
// 如果是鸟的话,返回鸟的翅膀的个数,如果是狗的话,返回狗的腿的个数
function getAnimal (x:Bird|Dog) {
if(isBird(x)) {
console.log(x.swing)
} else {
console.log(x.leg)
}
}
类型变换
// 交叉类型
// 将多个类型合转成一个类型
interface Bird {
name:string,
fly():void
}
interface Person {
name:string;
eat():void
}
// 取的是接口中属性的并集
type BirdMan = Bird & Person
// 里面的属性一个都不能少&, |可以少
let bm:BirdMan = {
name:'zhufeng',
fly(){}
eat(){}
}
// 兼容性检查,仅在传参的时候可以去那样判断,属性可以多,但赋值的时候不行
type People = {
name:string,
age:number
}
let p:People = {
name: 'zhufeng',
age: 10,
gender: 'male' // 属性不能多
}
// ----------------
let p = {
name:'zhufeng',
age: 10
}
// 用一个对象来定义一个类型,再用类型类限制变量p
type People = typeof p
let p:People ={
name: 'zhufeng',
age:10
}
// 索引访问操作符
interface Person{
name:string;
age:number
job: {
name:string
},
hobbies:{name:string, level:number}[]
}
let job:Person['job'] = {name: '前端'}
let hobbyLevel:Person['hobbies'][0]['level'] = 10
// keyof
// 索引类型查询操作符
interface Person {
name:string;
age:number;
gender: 'male'|'female'
}
function getValueByKey(p:Person,key:string) {
return p[key]
}
// 接口中的key
getValueByKey(p:Person,key:keyof Person) {
return p[key]
}
let p:Person = {
name: 'zhufeng',age:10,gender:'male'
}
let r = getValueByKey(p,'name') // zhufeng
// 映射类型
// 在定义的会后用in操作符去批量定义类型中的属性
interface Person {
name:string;
age:number;
gender?: 'male'|'female'
}
// ? 属性可选的意思
type PartPerson = {
[key in keyof Person]?:Person[key]
// 相当于每个属性后面加个?
}
let p:Person = {
name:'zhufeng',
age:10,
gender:
}
let p:PartPerson={
// 参数少传也不会报错
}
// partial 把属性变成可选
let p: Partial<Person> = {
}
// required 将传入的属性变成必选项,用-? 表示
// Reaonly 通过为传入的属性每一项都加上readonly修饰符来实现
// pick 能够从传入的属性中摘取某一项返回
Pick<Animal,'name'> // 从animal中去name属性
// 条件类型
// 在定义泛型的时候能够添加逻辑分支,以后泛型更加灵活
interface Fish {
name1:string
}
interface Water {
name2:string
}
interface Bird {
name3:string
}
interface Sky {
name4:string
}
type Conditon<T> = T extends Fish?Water:Sky
let con:Condition<Fish> = {name2: 'water'}
// 相当于 let con:Water = {name: 'water'}
网友评论