基础类型定义
string/number/boolean/null/undefined/void/symbol
const num: number = 123const name : string = "typescrit"
字面量
字面量的意思就是直接声明, 而非new关键词实例化出来的数据。
// 字面量const n:number = 123;const s:string = '456';const o:object = {a:1,b:'2'};// 非字面量const n:Number = new Number(123);const s:String = new String('456');const o:Object = new Object({a:1,b:'2'});
通过上面的例子, 大家应该看明白为什么ts中有些类型的开头字母是小写的。这是因为ts中用小写字母开头的类型代表字面量, 大写的是用来表示通过new实例化的数据。
类型的内容是固定的
type defType = 'a' | 'b' | 'c' | 'string';let type1 : defType = 'b';let type1 : defType = 'a';
类型定义
基本类型定义
const boy:{name:string;age:number;// 索引签名[props:sting]:string;} = {name:"tom",age: 12}
定义数组类型
const numArray: number[] = [1,2,3]const strArray: Array<string> = ["str1", "str2"]
定义函数,约束参数类型
const getTotal:() => number = () =>{ return 123; }// 定义函数类型的接口interface SearchFunc{(source:string, subString:string) : boolean;}let search:SearchFunc;mySearch = function(source: string, substring:string):boolean{let result = source.search(substring)return result > -1}
定义接口
接口可以实现一个对象包含多个类型。
interface IBoy {name:sting;age: number;}let xiaoming:IBoy ={name: "xiaoming",age: 16}
类实现接口,约束实例属性和方法
// 定义类接口,此接口约束的实例属性和方法。约束静态属性和方法需要用构造器接口interface AnimalInterface {name: string;legs: number;wings?: number;call(sound: string): void;}// 类实现接口,类就必须有接口的属性和方法class Dog implements AnimalInterface {legs: number = 0;constructor(public name: string, legs: number) {this.name = name;this.legs = legs;}call(sound: string): void {sound = '旺 旺 旺'}}class Bird implements AnimalInterface {constructor(public name: string, public legs: number, public wings: number) {this.legs = legs;this.wings = wings;}call(sound: string): void {sound = '唧 唧 唧 唧'}}let wangcai = new Dog("旺财", 4)let bage = new Bird("八哥", 2, 2)
类约束静态属性和方法,定义构造器接口
// 定义类接口,此接口约束的实例属性和方法。约束静态属性和方法需要用构造器接口interface AnimalInterface {name: string;legs: number;wings?: number;call(sound: string): void;}// 定义构造器接口interface AnimalConstructorInterface {new(name: string, legs: number, wings?:number): AnimalInterface;}// 创建动物工厂方法function createAnimal(constru:AnimalConstructorInterface,name: string, legs: number, wings?:number): AnimalInterface{// 这里就可以约束构造器的属性return new constru(name, legs, wings);}// 类实现接口,类就必须有接口的属性和方法class Dog implements AnimalInterface {legs: number = 0;constructor(public name: string, legs: number) {this.name = name;this.legs = legs;}call(sound: string): void {sound = '旺 旺 旺'}}class Bird implements AnimalInterface {name:string = 'Bird';legs:number = 0;// 对构造器的属性会进行强制约束constructor() {}call(sound: string): void {sound = '唧 唧 唧 唧'}}let wangcai = createAnimal(Dog, "旺财", 4)let bage = createAnimal(Bird, "八哥", 2, 2)
接口和接口的继承
interface Shape{color:string;}interface Points{num:number;size:number;}interface Square extends Shape, Points{area: number}// let s = {} as Squarelet s = <Square>{}s.color = 'red's.num = 3;
接口和类的继承
class Component {private state:string;}interface SelectableComponent extends Component{select():void;}class ButtonComponent extends Component implements SelectableComponent{// ButtonComponent实现了SelectableComponent接口,则必须实现接口中的方法。select():void{}}class TextComponent extends Component{select():void{}}// InputComponent出现类型错误,InputComponent没有继承Component,此处没继承到state属性,会出现错误class InputComponent implements SelectableComponent{select():void{}}
函数参数是对象
function add({ first = 1, second = [0] }: { first?: number; second?: number[]; }): number {return first + second.length;}console.log(add({ first: 1, second: [1, 2, 3] })); //4
TS类型保护机制
有时候在代码中多处添加类型断言,明确告知TS编译器的类型,会造成代码可读性低,增加维护难度。可以使用类型保护机制。有以下四种方法
// 共用的变量class Java {helloJava() {console.log("Hello Java")}}class JavaScript {helloJavaScript() {console.log("Hello JavaScript")}}// ts类型保护enum Type { Strong, Week };function getLanguage(type: Type, x: string | number = "default") {let lang = type === Type.Strong ? new Java() : new JavaScript()// ts中报错,无法识别属性helloJavaif(lang.helloJava){lang.helloJava()}else{lang.helloJavaScript()}return lang}
一:使用instanceof关键字创建区块
function getLanguage(type: Type, x: string | number = "default") {let lang = type === Type.Strong ? new Java() : new JavaScript()if(lang instanceof Java) {lang.helloJava()}else{lang.helloJavaScript()}return lang}
二:使用in 关键字
function getLanguage(type: Type, x: string | number = "default") {let lang = type === Type.Strong ? new Java() : new JavaScript()if("helloJava" in lang) {lang.helloJava()}else{lang.helloJavaScript()}return lang}
三:使用typeof 判断变量的基本类型
function getLanguage(type: Type, x: string | number = "default") {let lang = type === Type.Strong ? new Java() : new JavaScript()if(typeof x ==="string"){x = x.toUpperCase()}else{x = x.toFixed(2)}return { lang, x }}
四:使用类型保护函数
类型保护函数:返回值为类型谓词,格式: 参数 + is + 类型
function isJava(lang: Java | JavaScript): lang is Java{return (lang as Java).helloJava !== undefined}function getLanguage(type: Type, x: string | number = "default") {let lang = type === Type.Strong ? new Java() : new JavaScript()if(isJava(lang)){lang.helloJava()}else{lang.helloJavaScript()}return lang}
TS的高级类型-交叉类型
将多个类型合并成为一个新的类型,新类型具有所有类型的特性语法:typeA & typeB
多用于扩展类型的属性
// 交叉类型interface InterfaceA{methodA(): void;}interface InterfaceB{methodB(): void;}let aAndb:InterfaceA & InterfaceB = {methodA(){},methodB(){}}console.log(aAndb)
扩展类型的属性
function mixin<T extends object, K extends object>(o1:T, o2:U):T&U{return {...o1, ...o2}}let r = mixin({name:'北鸟南游', age: 3}, {address:"shanghai"})
TS的高级类型-联合类型
- ts中使用
!表示非空断言,表示该值一定不会为空 - ts中用
?.链式判断操作符,表示对象存在的情况下,获取对象的属性,如果对象不存在,则进行获取属性 - ts中
??表示第一个值非null或undefined,就取第一个值。用来排除null和undefined ```typescript // ! 表示值一定存在, ele不会为null let ele: HTMLElement |null = document.getElementById(“app”) ele!.innerHTML = “hello world”
// ?.的使用,aa && aa.xx && aa.xx.yy let a = {b: ‘b’} console.log(a?.b?.c)
// ??使用 123 ?? ‘save’ // 123 null ?? ‘save’ // save
<a name="atF5W"></a>#### 联合类型```typescriptinterface JavaInterface {helloJava(): void;build(): void;}interface JsInterface {helloJavaScript(): void;build(): void;}class Java implements JavaInterface {static Strong:boolean = true;helloJava() {console.log("Hello Java")}build(){console.log("build")}}class JavaScript implements JsInterface {static Strong:boolean = false;helloJavaScript() {console.log("Hello JavaScript")}build(){console.log("build")}}// ts类型保护enum Type { Strong, Week };function getLanguage(type: Type):JavaInterface|JsInterface {let lang = type === Type.Strong ? new Java() : new JavaScript();return lang}// getLanguage方法中lang对象为联合类型,此时lang的类型不能确定是Java还是JavaScript// 只能使用两个对象共有的属性/方法,如:build();// 此时调用helloJava或helloJavaScript则报错
可区分的联合类型
如果函数参数的类型是多个类型的联合类型,且多个类型间有一个共用属性,可以利用这个共用属性,创建出不同的类型保护区块
interface Square {kind: "Square";side: number;}interface Rectangle {kind: "Rectangle";width: number;height: number;}interface Circle{kind: "Circle";radius: number;}type Shape = Square | Rectangle | Circle;// 由于此时没有设置Circle类型的判断,如果s为Circle类型则会返回undefined。// TS报错:Function lacks ending return statement and return type does not include 'undefined'.function area(s: Shape): number {switch (s.kind){case 'Square':return s.side * s.sidecase 'Rectangle':return s.height * s.width}}
类型never类型,在switch中,将未捕获到的类型赋值给never,检查s是否是never类型。
function area(s: Shape) {switch (s.kind){case 'Square':return s.side * s.sidecase 'Rectangle':return s.height * s.width// 当缺少对Circle类型判断时,说明可以进入到default,s是Circle类型,并不是设置的never类型// TS报错:Argument of type 'Circle' is not assignable to parameter of type 'never'.default:return ((e: never) => {throw new Error(e)})(s)}}
正确的设置,switch分支对联合类型的所有类型都进行覆盖
function area(s: Shape) {switch (s.kind){case 'Square':return s.side * s.side;case 'Rectangle':return s.height * s.width;case 'Circle':return (s.radius**2) * Math.PI;default:return ((e: never) => {throw new Error(e)})(s)}}
TS的高级类型-索引类型
索引类型三个概念:
- 索引类型查询操作符
- keyof T:表示类型T,所有公共属性字面量的联合类型
- 索引类型访问操作符
- T[K]:表示对象T的属性K所表示的类型
- 范型约束
- T extends U:表示范型变量可以通过继承某个类型,获得属性
索引类型查询
TypeScript 允许我们遍历某种类型的属性,并通过 keyof 操作符提取其属性的名称。keyof 操作符是在 TypeScript 2.1 版本引入的,该操作符可以用于获取某种类型的所有键,其返回类型是联合类型。
keyof 与 Object.keys 略有相似,只不过 keyof 取 interface 的键。
除了接口外,keyof 也可以用于操作类
keyof T:表示类型T的所有属性的字面量的联合类型 ```typescript // 定义一个接口Obj含有属性a,b interface obj { age: number name: string } // 定义变量key,类型为keyof Obj —> “age” | “name” let key: keyof obj = “name” // key的类型为 “age” | “name”
- T extends U:表示范型变量可以通过继承某个类型,获得属性
// keyof操作类 class Person { name: string = “tom”; }
let sname: keyof Person; sname = “name”;
//keyof 操作对象 type A = keyof {a:1,b:’123’} // ‘a’|’b type B = keyof [1,2] // ‘0’|’1’|’push’…, 获取到内容的同时,还得到了Array原型上的方法和属性
keyof 操作符除了支持接口和类之外,它也支持基本数据类型:```typescriptlet K1: keyof boolean; // let K1: "valueOf"let K2: keyof number; // let K2: "toString" | "toFixed" | "toExponential" | ...let K3: keyof symbol; // let K1: "valueOf"
另外一种特殊情况,定义的接口属性不定。
interface Foo {[props: string]: string;}
此时Foo接口被认定为属性字段全部为string,但是由于js可以通过数字和字符串访问对象属性,因此keyof Foo的结果是string | number;
let f:key Foo = 123; //此时也不报错

如果想通过一个方法getKey获取对象里的属性值,通常的写法
let Boy = {name:"xm",age:10}function getKeys(o:object, k: string){return o[k];}
此时在return o[k]时,TS会报错,类型检验无法通过。
为了解决这个错误,可以使用范型加索引类型
let Boy = {name:"xm",age:10}function getKeys<T extends object, K extends keyof T>(o:T, name:K): T[K]{return o[name];}console.log(getKeys(Boy, "name"));
索引类型访问
语法:T[K],表示对象T的属性K的类型
interface Person {age: number;}let p:Person['age']; //p:number,p的类型为数字type Man = { name:'man'; age:12}let m:Man['name']; //m的类型为string
范型约束
T extends U:范型T可以继承对象U的属性
function prop<T extends object, K extends keyof T>(obj: T, keys: K): T[K]{return obj[keys];}
TS的高级类型-映射类型
映射类型:TS允许将一个类型映射成另外一种类型,有以下Readonly、Partial、Pick三种同源映射,和Record非同源映射。
Readonly 只读
将一个接口的所有属性映射为只读
interface Obj{a:number;b:string;c:boolean;}type ReadonlyObj = Readonly<Obj>/* TS编译之后type ReadonlyObj = {readonly a: number;readonly b: string;readonly c: boolean;} */let ReadonlyObjDemo:ReadonlyObj = {a:1,b:"readonly",c:true}ReadonlyObjDemo.a = 3;//error Cannot assign to 'a' because it is a read-only property.
Partical 可选
将一个接口的所有属性映射为可选
type PartialObj = Partial<Obj>/* TS编译之后type PartialObj = {a?: number | undefined;b?: string | undefined;c?: boolean | undefined;} */// a/b/c三个属性都是可选的let PartialObjDemo:PartialObj = {a:100}
Pick 摘取部分
摘取对象的一部分属性,形成新类型
type PickObj = Pick<Obj, "a" | "b"> //抽取了Obj对象的a、b属性,形成一个新的类型/* TS编译之后type PickObj = {a: number;b: string;} */let pickObjDemo:PickObj = {a:66,b:'66'}
非同源映射Record
引入新属性,将引入的属性的类型,设置为第二个参数。
/*Record 源码type Record<K extends keyof any, T> = {[P in K]: T;};*/// K为新增的属性,T为属性类型type RecordObj = Record<"x" | "y", Obj>// 定义一个对象包含x/y属性,这两个属性的类型为Obj类型/* TS编译之后type RecordObj = {x: Obj;y: Obj;} */let RecordObjDemo:RecordObj = {x:{a:1,b:"x",c:true},y:{a:2,b:"y",c:false},}
TS的高级类型-条件类型
TS提供了几种内置的预定义的条件类型
- Exclude
- 用于从类型T中去除不在U类型中的成员 - Extract
- 用于从类型T中取出可分配给U类型的成员 - NonNullable
- 用于从类型T中去除undefined和null类型 - ReturnType
- 获取函数类型的返回类型 - InstanceType
- 获取构造函数的实例类型 条件类型
条件类型是由条件表达式决定的类型,使类型具有不唯一性,增加TS语言灵活性。
T extends U ? X : Y;
如果类型T可被赋值给类型U,那么结果类型是X类型,否则是Y类型。 ```typescript // 条件类型 type TypeName= T extends string ? ‘string’ : T extends number ? ‘number’ : T extends boolean ? ‘boolean’ : T extends undefined ? ‘undefined’ : T extends Function ? ‘Function’ : ‘object’
// 定义类型T1为条件类型,传入参数string,指定t1为string类型
type T1 = TypeName
<br />T2传入的是数组,不在所属的条件中,则T2的类型为最后的object。<br /><a name="WSEBI"></a>#### 分步式条件类型当T是联合类型,结果类型变为多条件类型的联合类型<br />(A | B) extends U ? X : Y;<br />可以将A和B拆解<br />(A extends U ? X : Y) | (B extends U ? X : Y)这样定义的变量会被推断为联合类型```typescripttype T3 = TypeName<string | string[]>
分步式条件类型应用
主要应用是对类型进行过滤
如果T可以被赋值给U,结果类型为never类型,否则为T类型
// 如果T可以被赋值给U,结果类型为never类型,否则为T类型type Diff<T, U> = T extends U ? never : Ttype T4 = Diff<'a' | 'b' | 'c', 'a' | 'x'>

拆解逻辑分析
Diff会被拆解为多个条件类型的联合类型
type Diff<T, U> = T extends U ? never : Ttype T4 = Diff<'a' | 'b' | 'c', 'a' | 'e'>// 拆解分析:// Diff<'a', 'a' | 'e'> | Diff<'b', 'a' | 'e'> | Diff<'c', 'a' | 'e'>// never | 'b' | 'c'// 'b' | 'c'
- 先判断a是否可以被赋值给这个字面量联合类型’a’ | ‘e’,答案是可以的,所以返回never
- 继续,因为b不可以被赋值给字面量联合类型’a’ | ‘e’,所以返回b
- 继续,c不可以被赋值给’a’ | ‘e’,所以返回c
- 最后,never和b,c的联合类型为’b’ | ‘c’
可以实现从类型T中移除不需要的类型,如undefined和null,定义一个NotNull,从T中过滤掉undefined和null
// Diff扩展:从T中过滤掉undefined和nulltype NotNull<T> = Diff<T, undefined | null>// 过滤掉undefined和null,T5的类型就变成了string和numbertype T5 = NotNull<string | number | undefined | null>

上述实现过程,在TS中有内置的方法:Extract和Exclude和NonNullable以及ReturnType和InstanceType;
Exclude作用是从类型T中过滤掉可以赋值给类型U的类型
Extract作用是可以从类型T中抽取出可以赋值给U的类型
// Extract<T, u>和Exclude<T, U>type T6 = Extract<'a' | 'b' | 'c', 'a' | 'e'>type T7 = Exclude<'a' | 'b' | 'c', 'a' | 'e'>


TS中Extract和Exclude的源码分析
/*** Exclude from T those types that are assignable to U*/type Exclude<T, U> = T extends U ? never : T;/*** Extract from T those types that are assignable to U*/type Extract<T, U> = T extends U ? T : never;
NonNullable,排除掉所有空的类型
// NonNullable,排除掉所有空的类型type T9 =NonNullable<string | number | undefined | null>

NonNullable源码
/*** Exclude null and undefined from T*/type NonNullable<T> = T extends null | undefined ? never : T;
ReturnType
// ReturnType<T>可以获取一个函数返回值的类型type T8 = ReturnType<() => string>

TS中ReturnType源码:
/*** Obtain the return type of a function type*/type ReturnType<T extends (...args: any) => any>= T extends (...args: any) => infer R ? R : any;
实现分析:
T extends (…args: any) => any:ReturnType要求参数T可以赋值给一个函数,这个函数有任意的参数,返回值类型也是任意的
由于函数返回值类型不确定,这里使用了infer关键字,表示待推断,延迟推断,需要根据实际的情况确定
infer R ? R : any:
如果实际类型是R,那么结果类型就是R,否则返回值类型就是any
InstanceType
class CDemo {constructor(x:number,y:string){}}type CType = InstanceType<typeof CDemo>

TypeScript源码的实现
/*** Obtain the return type of a constructor function type*/type InstanceType<T extends new (...args: any) => any> = T extends new (...args: any) => infer R ? R : any;
对象的空值合并运算符
有时,在访问属性时 TypeScript 会自动帮你插入可选链操作符。类似 let t = myObj?.property,这样的话变量 t 将被赋值为 property 或 undefined。
let t = myObj?.text ?? " "
如果myObj下没有属性text则会返回undefined,然而有些情况不希望返回的是undefined,可以用??设置默认值。
?. 可选链操作符
?? 空值合并运算符
数组和元组
数组
const arr:(number | string)[] = [1,'a',2]const stringArr:string[] = ['a', 'b', 'c']
数组存储对象类型
// type alias 类型别名type student = {name:string,age:number}const objArr:student[] = [{name: 'ts',age:12}]
元组 tuple
const teacher:[string, string, number] = ['chinese','grey',21]
接口 interface
出现重复定义的类型判断
const getName = (person: {name:string}): string => {return person.name;};const setName = (person: {name:string}, name: string): void => {person.name = name;};
上面代码重复出现name:string的定义。可以定义成接口
interface IPerson {name: string;}const getName = (person: IPerson): string => {return person.name;};const setName = (person: IPerson, name: string): void => {person.name = name;};
- 接口中使用?定义可选参数
- 使用readonly定义只读参数 ```typescript interface IPerson { name: string; readonly gender: string; age? : number; }
const getName = (person: IPerson): string => { return person.name; };
<a name="6c8d5cc957d0eae4a6bc228dc913ce01"></a>### 定义一些新增属性```typescriptinterface IPerson {name: string;age: number;[props: string]: any;}const getName = (person: IPerson): string => {return person.name + person.age;};getName({name: "ts",age: 1,other: true,});
接口定义方法
interface IPerson{name:string;say():string;}const getName = (person: IPerson): string => {return person.name;};getName({name:'ts',say(){return 'hello ts!'}})
类class 实现接口
interface IPerson{name:string;say():string;}class Student implements IPerson{public name:string;say(){return "我叫:" + this.name;}}
接口的继承
interface IPerson{name:string}interface IStudent extends IPerson {grade: string;}const xiaoming: IStudent = {name: "xiaoming",grade: "五年级"};
函数Function
函数的参数类型定义
const add=(x:number, y:number, z?:number): void =>{if(typeof z=== 'number'){return x+y+z;}else{return x + y}}interface ISum{(x:number, y:number, z?:number):number;}let add1:ISum = add;
可选参数/默认参数
// 使用?表示可选参数,可选参数必须放后边位置const userName=(firstName:string, lastName?:string):string => {return firstName + lastName}// 默认参数,可以给参数设置一个默认值const defaultUserName=(firstName:string="shen", lastName?:string):string => {return firstName + lastName}
重载
同一个函数赋值不同个数的参数,或者参数的类型不同
function setParams(parasA:string):string{return parasA.toUpperCase();}function setParams(parasA:string,paramsB:number):string{return parasA.repeat(paramsB)}
定义类class
- 使用extends继承父类
- 重写父类方法时,使用super可以调用父类中定义的方法 ```typescript class Language { name: string = “ts”; work() { return “writing “ + this.name; } } class Front extends Language { work() { return “前端开发” + super.work(); } } let l1 = new Language(); console.log(l1.work());
let l2 = new Front(); console.log(l2.work());
<a name="96d5f3bf06f8b8fed47f3a1c2a8f6fb2"></a>### 定义属性时,设置访问类型修饰符- private 私有- protected 子类可用- public 公用```typescriptclass Person {constructor(public name:string, protected age: number, private password:string){this.name = name;this.age = age;this.password = password;}}
readonly只读,不可修改
class Person {constructor(public readonly name:string){this.name = name;}}let sam = new Person("sam")//无法分配到 "name" ,因为它是只读属性sam.name = "xxx"
constructor构造器
class Man {// 传统写法// public name:string;// constructor(name:string){// this.name = name;// }// 简化写法constructor(public name: string) {}}
构造器中参数的继承,super关键字
class Man {constructor(public name: string) {}work(n:string){console.log(n)}}class Boy extends Man {constructor(public age: number) {// 可以继承父类的name属性super("ts");}getBoyInfo() {return `${this.name} + ${this.age}`;}work(n:string){console.log("boy work");// 可以用super关键字调用父类的方法super.work(n)}}let b = new Boy(1);
静态属性
getter & setter属性定义的为原型属性
- private定义的私有属性,外部无法访问,可以使用getter和setter解决
- 内部私有属性一般定义为 “_属性”
getter/setter定义时看似为函数,实际调用时是作为属性
class Person {constructor(private _name: string) {}get name() {return this._name + "加密";}set name(name: string) {this._name = name;}}let p = new Person("ts");console.log(p.name);p.name = "js";console.log(p.name);
static静态属性,类本身的属性
属性是通过类直接调用,不用实例化调用
class Instance {private static _instance: Instance;// private构造器,不能实例化类private constructor() {}static getInstance() {if (!this._instance) {this._instance = new Instance();}return this._instance;}}// 通过类Instance直接调用getInstance方法const demo1 = Instance.getInstance();const demo2 = Instance.getInstance();console.log(demo1 === demo2);
抽象类abstract
多个类要同时实现一个方法,可以把该方法定义为一个抽象类中的抽象方法
- 抽象类只能被继承,不能被实例化
- 抽象类中的抽象方法,不能写具体的实现
- 继承了抽象类的类,必须实现抽象类中的抽象方法 ```typescript abstract class Gemo { getType() { return “Gemo type.”; } abstract getArea(): number; }
class Circle extends Gemo { constructor(public radius: number) { super(); } getArea() { return this.radius Math.PI; } } class Square extends Gemo { constructor(public width: number) { super(); } getArea() { return this.width this.width; } }
<a name="b3b86024c7a3631f62a16a743ffd4938"></a>## 泛型泛型的意义在于函数的重用性,设计原则希望组件不仅能够支持当前的数据类型,同时也能支持未来的数据类型<br />根据业务最初的设计,函数identity入参为string```javascriptfunction identity(arg: String){return arg}console.log(identity('100'))
但是随着业务迭代过程,参数需要支持number
function identity(arg: String){return arg}console.log(identity(100)) // Argument of type '100' is not assignable to parameter of type 'String'.
注意尽量避免使用any,any会丢失一些信息,我们无法确定返回值是什么类型。
泛型可以保证入参和返回值是相同类型,泛型是一种特殊的变量,只用于表示类型而不是值。
语法:(arg:T):T 其中T为自定义变量
const hello : string = "Hello vue!"function say<T>(arg: T): T {return arg;}console.log(say(hello)) // Hello vue!
泛型约束,
使用interface和extends进行类型约束
泛型无法保证每种类型都有某一属性
const hello : string = "Hello vue!"function say<T>(arg: T): T {console.log(arg.length) // Property 'length' does not exist on type 'T'.return arg;}console.log(say(hello)) // Hello vue!
要在约束层面上就提示错误,需要定义一个接口来描述约束条件
interface Lengthwise {length: number;}function say<T extends Lengthwise>(arg: T): T {console.log(arg.length)return arg;}console.log(say(1)) // Argument of type '1' is not assignable to parameter of type 'Lengthwise'.console.log(say({value: 'hello vue!', length: 10})) // { value: 'hello vue!', length: 10 }
范型变量
范型T可作为变量,传入到args的类型定义中。
function getLength<T>(args:T[]):T[]{console.log(args.length)return args;}
范型类型
function identity<T>(args:T):T{return args}interface setIdentityFn<T>{(args:T) : T}let myIdentity: setIdentityFn<number> = identity
范型约束,约束对象的属性,
使用extends和对象的keyof
function getProperty<T, U extends keyof T>(obj:T, key: U):T[U]{return obj[key]}let test = {a:1,b:2,c:3,d:4}console.log(getProperty(test,"a"))// test不包含e属性,调用就会报错// console.log(getProperty(test,"e"))
类使用范型
定义的类,也可以进行范型约束,约束内部变量data的类型。通过新建类时指定传入的类型。
class Queue<T>{private data:T[] = []push(item:T){return this.data.push(item)}pop():T{return this.data.shift()}}const queue = new Queue<number>()queue.push(66)console.log(queue.pop().toFixed())
接口interface的范型约束
interface KeyPair<T, U>{key : Tvalue: U}let k1: KeyPair<string, number> = {key: "str", value:100}let k2: KeyPair<number, string> = {key: 66, value: "string"}
高级类型
联合类型
interface JavaInterface {helloJava(): void;build(): void;}interface JsInterface {helloJavaScript(): void;build(): void;}class Java implements JavaInterface {static Strong:boolean = true;helloJava() {console.log("Hello Java")}build(){console.log("build Java")}}class JavaScript implements JsInterface {static Strong:boolean = false;helloJavaScript() {console.log("Hello JavaScript")}build(){console.log("build JavaScript")}}// ts类型保护enum Type { Strong, Week };function getLanguage(type: Type):JavaInterface|JsInterface {let lang = type === Type.Strong ? new Java() : new JavaScript();return lang}let j = getLanguage(Type.Strong)// 只可以调用联合类型console.log(j.build());// 下面两个调用都会报错// console.log(j.helloJava(),j.helloJavaScript());
交叉类型
// 交叉类型 a&bfunction combine<T, U>(objA:T, objB:U):T & U{let result = {} as T & U;for(let id in objA){result[id] = objA[id] as any}for(let id in objB){if(!result.hasOwnProperty(id)){result[id] = objB[id] as any}}return result;}class Person{constructor(public name:string){this.name = name;}}interface logger{log(): void;}class ConsoleLog implements logger{log(){console.log("class ConsoleLog ");}}let sam = combine(new Person("sam"), new ConsoleLog())console.log(sam.name, sam.log());
类型保护
1.类型断言as
通过as断言,判断对象的类型
interface Bird{fly(height: number): void;layEggs(num: number): string;}interface Fish{swim(): boolean;layEggs(num: number):string;}class BirdC implements Bird {fly(height: number): void{console.log(`fly ${height}m`);}layEggs(num: number):string{return `lay ${num} eggs`}}class FishC implements Fish {swim(): boolean{console.log("can swim");return true;}layEggs(num: number):string{return `lay ${num} eggs`}}type Pet = FishC | BirdC;function getPet(p:Pet):Pet{return p}let pet = getPet(new BirdC())let petFish = getPet(new FishC())// 这里只能使用共用的属性console.log(pet.layEggs(6));// 要想使用不同的属性,可以使用as断言if((pet as FishC).swim){(pet as FishC).swim()}if((pet as BirdC).fly){(pet as BirdC).fly(10)}if((petFish as FishC).swim){(petFish as FishC).swim()}
2.类型谓词 instance is classA
type Pet = FishC | BirdC;// 可以使用实例is类,判断对象的类型,进行类型保护,然后就可以调用不同类型的方法function isFish(pet: Fish | Bird): pet is Fish{return (pet as Fish).swim !== undefined;}function getPet(p:Pet):Pet{if(isFish(p)){p.swim();} else {p.fly(8);}return p}let pet = getPet(new BirdC())let petFish = getPet(new FishC())
3:typeof判断基本类型
function getLanguage( x: string | number = "default") {if(typeof x ==="string"){x = x.toUpperCase()}else{x = x.toFixed(2)}return { x }}
4:instanceof判断对象
type Pet = FishC | BirdC;function getPet(p:Pet):Pet{if(p instanceof FishC){p.swim();} else {p.fly(8);}return p}
5:in关键字
type Pet = FishC | BirdC;function getPet(p:Pet):Pet{if("swim" in p){p.swim();} else {p.fly(8);}return p}
extends keyof和in keyof的区别
type Partial<T> = {[P in keyof T]?: T[P];};
type Pick<T, K extends keyof T> = {[P in K]: T[P];};
