// 交叉类型: 取全部类型的并集 interface DogInterface { run(): void } interface CatInterface { jump(): void } let pet:DogInterface & CatInterface = { run(): void jump(): void }
联合类型数组
// 联合类型就是声明的类型并不肯定,能够为多个类型中的一个 let a: string | number = "1" // 字面量联合类型 // 有时候咱们不只要限制变量的类型,还须要限制变量的取值在某个特定的范围内 // 字符串联合类型 let c: 'a' | 'b' | 'c' // 数字联合类型 let b: 1 | 2 | 3 // 对象联合类型 class Dog implements DogInterface{ run(): void eat(): void } class Cat implements CatInterface{ jump(): void eat(): void } enum Master { Boy, Girl } function getPet(master: Master){ let pet = master === Master.Boy ? new Dog(): new Cat() // 若是一个对象是联合类型,在类型未肯定的状况下,只能访问全部类型的公有成员,这种状况下只能访问全部类型的交集 pet.eat() return pet; }
interface Square { kind: "sauare", size: number } interface RectAngle { kind: "rectAngle", width: number, height: number } interface Circle { kind: "circle", r: number } type Shape = Square | RectAngle | Circle function area(s: Shape) { switch (s.kind) { case: sauare: return s.size * s.size case: rectAngle: return s.height * s.width case: circle: return Math.PI * s.r * s.r } } // 没有创建circle保护区块,不会报错 console.log(area({kind: 'circle', r: 2})) // 解决此问题方法1:加返回值: number, // 此时ts会判断,全部的switch分支是否是包含了全部的状况; function area(s: Shape): number // 解决此问题方法2: default: return ((e: never) => {throw newError(e)})(s) // 此函数的做用就是: // 检查s是否是never类型,若是是never类型,说明前面的全部分支都被覆盖了, 那么这个分支永远不会走到; // 若是s不是never类型,就说明前面的分支有遗漏,须要补上那个分支
// key的类型是a和b的字面量联合操做类型 interface Obj { a: number; b: string; } let key: keyof Obj
let value: Obj["a"]
let obj = { a: 1, b: 2, c: 3 } function getValues(obj: any, keys: string[]){ return keys.map(key => obj[key]) } console.log(getValues(obj, [a, b])) // 1,2 console.log(getValues(obj, [e, f])) // [undefind, undefind] // 此时ts应该报错 // 通过改造后的函数 function getValues<T,K extends keyof T>(obj: T,keys: K[]): T[K][]{ // 先定义个泛型变量T,用它来约束obj // 再定义一个泛型变量K,用它来约束keys数组 // 把 K 增长一个类型约束,让他来继承obj全部属性的联合类型,K extends keyof T; // 函数的返回值是一个数组[],数组的类型就是属性K对应的类型T[K] // 这样就经过一个索引类型把getValues改造完毕了,这时ts类型检查就发挥做用了 // 若是咱们指定一个再也不obj范围内的属性,ts就会报错 }
interface Obj { a: string; b: number; c: boolean; } // 好比把接口中的全部属性变为只读; type ReadOnlyObj = Readonly<Obj> // 把接口中的全部属性变成可选 type PartialObj = Partial<Obj> // 抽取接口中的子集 type PickObj = Pick<Obj,'a' | 'b'> // 同态 // 意思是只会做用于Object属性而不会引入新的属性; // 非同态类型 type RecordObj = Record<'x' | 'y', Obj> // 定义一个类型别名ReadOnlyObj // 接口名称就是Readonl y // 接口中传入的类型就是Obj // ts内置类库Readonly type Readonly<T> = { readonly [P in keyof T]: T[P] } 1. Readonly是一个泛型接口,并且是一个可索引类型的泛型接口 2. 索引签名是 P in keyof T 3. in keyof T 是一个 索引类型的查询操做符,表示T全部属性的联合类型 4. p in 这里执行了一次for in 操做;它会把变量P依次绑定到T的全部的属性上 5. 索引签名的返回值就是索引操做符了T[P],表明属性P 所指定的类型了; 6. 最后加上readonly,就把全部的属性变成了只读;
T extends U ? X : Y // 若是类型T,能够被赋值给类型U,结果类型就是X类型,不然Y类型; // 条件类型使类型具备了不惟一性,同时也增长了语言的灵活性; type TypeName<T> = T extends string ? 'string' : T extends number ? 'number' : T extends boolean ? 'boolean' : T extends undefined ? 'undefined' : T extends Function ? 'Function' : "object"; type T1 = TypeName<string> // string type T2 = typeName<string[]> // object
(A | B) extends U ? X : Y // (A extends U ? X : Y) | (B extends U ? X : Y) // 若是T 是一个联合类型的状况下,这时候的结果类型就会变成多个条件类型的联合类型; type T3 = TypeName<string | string[]> // "string" | "object" // T3就会被推断为string和object的字面量联合类型 // 利用这个特性能够帮咱们实现一些类型的过滤; type Diff<T, U> = T extends U ? never : T type T4 = Diff<"a" | "b" | "c", "a" | "e"> // type T4 = "b" | "c" // Diff会被拆解为多个条件类型的联合类型: // Diff<"a", "a" | "e"> | Diff<"b", "a" | "e"> | Diff<"c", "a" | "e"> // never | "b" | "c" // "b" | "c" // 做用:能够过滤掉类型T中能够赋值给类型U的类型 // 能够基于类型 Diff中再作扩展,从类型中除去咱们不须要的类型,好比:null 和 undefind type NotNull<T> = Diff<T, undefind | null > type T5 = NotNull<string | number | undefind | null> type T5 = string | number
type T6 = Extract<"a", "b", "c", "a" | "e"> // a
type RetrunType<T extends (...args: any) => any> = T extends (...args: any) => infer R ? R : any; // 1. RetrunType 要求参数 T 能够被赋值给一个函数 // 2. 这个函数的参数和返回值都是any; // 3. 因为这里函数的返回值是不肯定的,用了一个infer关键字 // 4. infer的关键字的做用是待推断或者延迟推断,须要根据实际状况来肯定 // 5. 若是实际状况是R,则返回R,不然返回值类型就是any;