>[success] # 高级类型 >[danger] ##### 交叉类型 ~~~ 1.交叉类型:我们可以把现有的多种类型叠加到一起成为一种类型,它包含了所需的所有类型的特性,取多个类型的并集 简单的说' 交叉类型是将多个类型合并为一个类型',用'&' 来表示 2.下面的案例是将两个对象合并,并且返回合并后的对象 ~~~ ~~~ const mergFunc = <T,U>(arg1:T,age2:U):U&T=>{ let res = {} as U&T res = Object.assign(arg1,age2) return res } mergFunc({a:1},{b:2}) ~~~ >[danger] ##### 联合类型 ~~~ 1.联合类型表示一个值可以是几种类型之一,用'|' 来表示 ~~~ ~~~ const getLenFunc = (content:number|string)=>{ if (typeof content === 'string'){ return content.length} else {return content.toString().length} } // getLenFunc(true) // 报错 只能是string 或者是number类型中的一个 ~~~ * 接口用联合类型 ~~~ 1.如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员 简单的说'使用一个联合类型的参数只能使用共有成员',下面案例中c虽然有age属性,但是会报错 只能使用他们的共有成员name ~~~ ~~~ interface a { name:string, age:number } interface b { name:string, sex:string } // const c:(a|b) = { // age:13, // sex:'nan' // } // 报错 const c:(a|b) = { name:'fff', age:12, sex:'w' } // c.age// 报错 c.name ~~~ >[danger] ##### 类型守卫 ~~~ 1.第一种使用方法封装 ,方法返回值的格式 是 '值' is '类型' 2.第二中是直接使用type of ,但只能针对"number","string","boolean"或"symbol", 这里的针对要解释一下,首先type of 是js 的,所以 写type of === ‘object’是可以的 ,但是不会自带自动后往后的判断,下面案例为例'判断了string'类型else 自动判断就是 number 类型,如果是判读是非这四种类型,else 还需要要自己处理 3.如果是类用instance of 作为类型保护 ~~~ * 什么场景实用 ~~~ const getRandomValue = (list:(string|number)[])=>{ const randomNumber = Math.random() * 10 if (randomNumber>5) return list[0] else return list[1] } const item = getRandomValue([1,'w']) // 直接这些写会报错 // 首先第一个问题是返回值的类型不确定是字符还是数字 // 数字是没有length 属性的所以这么写的判断ts 会报错的 // if(item.length){ // console.log(item.length) // }else{ // console.log(item.toFixed()) // } // 解决第一种使用类型断言 // 缺点 每一个item 都要使用对应的类型断言 if ((item as string).length ){ console.log((item as string).length) }else{ console.log((item as number).toFixed()) } ~~~ * 封装方法--用户自定义的类型守卫 (复杂情况用这种) ~~~ const getRandomValue = (list:(string|number)[])=>{ const randomNumber = Math.random() * 10 if (randomNumber>5) return list[0] else return list[1] } const item = getRandomValue([1,'w']) // 类型保护 // 注意 这里的is 可以是任意类型,可以是自己定义的接口类型 // 这里是因为需求是string 类型,并且返回相当于必须是一个boolean类型 function isString (value:number|string):value is string{ return typeof value === 'string' } if (isString(item)) { console.log(item.length) } else { console.log(item.toFixed()) } ~~~ * typeof类型守卫(简单情况用这种) ~~~ const getRandomValue = (list:(string|number)[])=>{ const randomNumber = Math.random() * 10 if (randomNumber>5) return list[0] else return list[1] } const item = getRandomValue([1,'w']) // 简单的逻辑直接typeof 即可 // 比较的类型只能是"number","string","boolean"或"symbol" if (typeof item === 'string') { console.log(item.length) } else { console.log(item.toFixed()) } ~~~ * instanceof 类型保护 ~~~ interface Padder { getPaddingString(): string } class SpaceRepeatingPadder implements Padder { constructor(private numSpaces: number) { } getPaddingString() { return Array(this.numSpaces + 1).join(" "); } } class StringPadder implements Padder { constructor(private value: string) { } getPaddingString() { return this.value; } } function getRandomPadder() { return Math.random() < 0.5 ? new SpaceRepeatingPadder(4) : new StringPadder(" "); } // 类型为SpaceRepeatingPadder | StringPadder let padder: Padder = getRandomPadder(); if (padder instanceof SpaceRepeatingPadder) { padder; // 类型细化为'SpaceRepeatingPadder' } if (padder instanceof StringPadder) { padder; // 类型细化为'StringPadder' } ~~~ >[danger] ##### null 和undefined ~~~ 1.在tsconfig.json 设置 'strictNullChecks':当你声明一个变量时,它不会自动地包含null或undefined。 ~~~ * 如果设置是true ~~~ let s = "foo"; s = null; // 错误, 'null'不能赋值给'string' sn = undefined; // 错误, ~~~ * 如果设置false ~~~ let s = "foo"; s = null; //可以 sn = undefined; // 可以 ~~~ * 如果设置了false 在可选参数的时候 是只能自动添加一个undefined ~~~ function f(x: number, y?: number) { return x + (y || 0); } f(1, 2); f(1); f(1, undefined); f(1, null); // error, 'null' is not assignable to 'number | undefined' ~~~ >[danger] ##### 类型断言 ~~~ 1.如果编译器不能够去除null或undefined,你可以使用类型断言手动去除。 语法是添加!后缀: ~~~ ~~~ function broken(name: string | null): string { function postfix(epithet: string) { // return name.charAt(0) + '. the ' + epithet; // 报错 有可能是null return name!.charAt(0) + '. the ' + epithet; // 排除是null } name = name || "Bob"; return postfix("great"); } ~~~ >[danger] ##### 类型别名 -- type ~~~ 1.'类型别名':给 这个类型起一个新的名字,简单的理解 和js 一样是给重复使用的变量起一个统一的名字调用,只不过ts规定的是类型 2.可以是原始值,联合类型,元组以及其它任何你需要手写的类型 ~~~ ~~~ type Name = string type NameResolver = ()=>string // 联合类型 type NameOrResolver = Name | NameResolver function getName(n: NameOrResolver): Name { // 类型保护 所以else 会自动知道另外一个类型 if (typeof n === 'string') { return n; } else { return n(); } } let names:Name = 'w' // 相当于这个name 是string 类型 ~~~ * 类型别名可以向接口一样使用泛型 ~~~ // 也可以像接口一样使用泛型 type c<T> = {name:T,age:T} ~~~ * 利用类型别名生成树型结构 ~~~ 1.这里要注意,child 字段必须是可选类型,否则就会让你一直写下去,而且这种自己用自己 只能出现在对象字段中,不能像下面这样的写法'type Yikes = Yikes[]; // error' ~~~ ~~~ // 创建属性结构的时候可以自己引用自己 type Childs<T>= { current:T, child?:Childs<T> // 可选参数 } let tree:Childs<string> = { current:'first', child:{ current:'second', child:{ current:'third' } } } ~~~ * 交叉类型使用 ~~~ type LinkedList<T> = T & { next: LinkedList<T> }; interface Person { name: string; } var people: LinkedList<Person>; var s = people.name; var s = people.next.name; var s = people.next.next.name; var s = people.next.next.next.name; ~~~ >[danger] ##### 类型别名和接口的区别 ~~~ 1.'类型别名'不能被extends和implements(自己也不能extends和implements其它类型) 2.如果你无法通过接口来描述一个类型并且需要使用联合类型或元组类型,这时通常会使用类型别名。 ~~~ >[danger] ##### 字符串字面量 和 数字类型 ~~~ 1.指定具体字符串或数字 ~~~ ~~~ type Type= 'w'|1|'s' // let type:Type = 6 //报错 只能是 w 1 或者 s ~~~ >[danger] ##### 可辨识联合 ~~~ interface Square { kind: "square"; size: number; } interface Rectangle { kind: "rectangle"; width: number; height: number; } interface Circle { kind: "circle"; radius: number; } type Shape = Square | Rectangle | Circle; // 可辨识联合 function area(s: Shape) { switch (s.kind) { case "square": return s.size * s.size; case "rectangle": return s.height * s.width; case "circle": return Math.PI * s.radius ** 2; } } ~~~ >[danger] ##### 完整类型检查 ~~~ 1.这是时候其实我们代码是有问题的如果传入的是'Triangle' 类型整个代码就会出现问题 ~~~ ~~~ type Shape = Square | Rectangle | Circle | Triangle; function area(s: Shape) { switch (s.kind) { case "square": return s.size * s.size; case "rectangle": return s.height * s.width; case "circle": return Math.PI * s.radius ** 2; } // should error here - we didn't handle case "triangle" } ~~~ * 解决这种帮助自动检出有未用类型错误 ~~~ 1.启用tsconfig--strictNullChecks并且指定一个返回值类型 ~~~ ~~~ function area(s: Shape): number { // error: returns number | undefined switch (s.kind) { case "square": return s.size * s.size; case "rectangle": return s.height * s.width; case "circle": return Math.PI * s.radius ** 2; } } ~~~ ~~~ 2.使用never类型,编译器用它来进行完整性检查 ~~~ ~~~ function assertNever(x: never): never { throw new Error("Unexpected object: " + x); } function area(s: Shape) { switch (s.kind) { case "square": return s.size * s.size; case "rectangle": return s.height * s.width; case "circle": return Math.PI * s.radius ** 2; default: return assertNever(s); // error here if there are missing cases } } ~~~