我是靠谱客的博主 糟糕胡萝卜,这篇文章主要介绍【TS基础】个人学习记录6-零碎概念之类型推论、联合类型、类型断言、类型别名、字符串字面量、交叉类型类型推论联合类型类型断言类型别名字符串字面量交叉类型类型缩减,现在分享给大家,希望可以做个参考。

文章目录

  • 类型推论
  • 联合类型
  • 类型断言
    • 联合类型的断言
    • 继承断言
    • 可访问不存在属性
    • 断言为具体接口类型
    • 补充
  • 类型别名
  • 字符串字面量
  • 交叉类型
    • 合并接口
    • 交叉联合类型
  • 类型缩减

类型推论

复制代码
1
2
3
let str = 'str' console.log(typeof str)

当没有指定变量类型的时候,ts会自动去推导类型。


联合类型

复制代码
1
2
3
4
5
6
7
let numOrStr: number | string // 这个变量可以是数字也可以是字符串 numOrStr = 'abc' numOrStr = 123 // 联合类型还可以直接时字符串 let str : 'a' | 'b' | 'c' str = 'b'

在函数入参内使用联合类型的时候,要注意这样的变量只能访问联合类型的共有属性和方法:

复制代码
1
2
3
4
5
// 函数作用,当入参类型是字符串时,返回长度 fuction getLength(val: string | number): number { // 因为length属性不是字符串和数字的共同属性,当你直接return val.length时,ts就会报错提示你,可能val为number类型没有length属性 }

这时就需要类型断言去解决了。


类型断言

它建议编译器将变量可以视为某种类型。

有两种写法:

复制代码
1
2
3
as 类型 // 推荐写这种,因为更符合JSX的写法 <类型>

主要是为了解决一些类型判断的问题,例如以下。

联合类型的断言

前面说到联合类型的一个小缺点的解决方法。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
fuction getLength(val: string | number): number { const str = val as string // 注意并不是类型转换,而是当做某类型去看 if (str.length) { // 如果是数字类型是没有length属性的 return str.length } else { const num = val as number return num.toString().length } } // 或(推荐) fuction getLength(val: string | number): number { if (typeof val === 'string') { return val.length } else { return val.toString().length } }

还可以结合接口:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
interface Cat { name: string; run(): void; } interface Fish { name: string; swim(): void; } function isFish(animal: Cat | Fish) { // if (typeof animal.swim === 'function') { 这样写会报错,因为不一定会传Fish类型进来 // return true; // } if (typeof (animal as Fish).swim === 'function') { // 这样就给编译器指定参数为Fish类型,就不会报错了 return true; } return false; }

继承断言

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
class ApiError extends Error { code: number = 0; } class HttpError extends Error { statusCode: number = 200; } function isApiError(error: Error) { if (typeof (error as ApiError).code === 'number') { return true; } return false; }

主要解决这样一个场景:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
interface ApiError extends Error { code: number; } interface HttpError extends Error { statusCode: number; } function isApiError(error: Error) { if (error instanceof ApiError) { // 因为编译成es5后,接口的定义被去掉了,此时instanceof找不到ApiError所以报错 return true; } return false; }

可访问不存在属性

复制代码
1
2
3
4
5
window.foo = 1; // window 上添加一个属性 foo时,TS会报错,说不存在这个属性 (window as any).foo = 1; // 断言成any类型后就可以添加了

注意:还是不推荐用any来操作

断言为具体接口类型

未来补充

补充

前面解释了个人对断言的理解是:它建议编译器将变量可以视为某种类型。

所以并不是所变量一定要符合as后面的类型才能接着往下走:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
interface P { a: number; } function fn(x: object) { if (x as P) { return console.log("断言成功"); } else { return console.log("不走断言"); } } fn({ b: "1" }); // "断言成功" 记住啦

类型别名

就是给类型的定义取个名

复制代码
1
2
3
4
5
type strOrNum = string | number // strOrNum 这个类型别名就包含了string或者number let str: strOrNum = '123' // 等同于 let str: string | number = '123'

通常用于给一些定义做一个打包!

例如定义了一个数组子项的接口类型,可以这样打包成一个数组类型:

复制代码
1
2
3
4
5
interface element { name: string } type Arr = element[]

结合联合类型也是有很好的使用场景:

复制代码
1
2
3
4
type gender = "man" | "women" type age = "young" | "old" type person = gender | age // 类型就综合了"man" | "women" | "young" | "old"

很多时候,类型别名都可以代替接口,因为方便,而且可以直接声明元组、联合类型、交叉类型、原始类型,也包括对象。

但是与接口不同的是,接口在同一个地方可以多次定义,定义的内容会累加起来,而同一个类型别名就不能多次被定义,会报错。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
interface Language { id: number; } interface Language { name: string; // id: number 不能和上面有同样的哦,会报错 } let lang: Language = { id: 1, name: 'name' } // 这样是没问题的 // 如果把interface换成type就会报错

字符串字面量

例如:

复制代码
1
2
3
let str: 'name' = 'name' // 等于name以外的都会报错 let num : 2 = 2 // 等于2之外的都会报错

有限定字符串的作用,当配合类型别名后可以这样

复制代码
1
2
3
type Directions = 'Up' | 'Down' | 'Left' | 'Right' let toWhere: Directions = 'Up' // 这个变量的值被限定在Directions里面这四个了,写成其他的会报错

或者配合接口:

复制代码
1
2
3
4
5
interface tools = { type: 'stick' | 'scissors' | 'knife' } // 根据这个接口创建的变量,type属性只能写上面那三个。

等等。


交叉类型

和联合类型有点对立的意思,就是把两个东西合并起来,例如:

复制代码
1
2
type what = string & number // 意思是这个类型别名既是string 类型又是 number 类型,但我们知道这个是不存在的,所以ts把what 定义为了never

合并接口

那这个交叉类型有什么用处呢?

我们可以用来合并接口

复制代码
1
2
3
4
5
6
7
8
9
10
interface IName { name: string } type Iperson = IName & { age: number } // 这时候Iperson就变成 interface Iperson { name: string age: number }

注意!合并的时候要看里面是不是存在不同类型合并出现never的问题:

复制代码
1
2
3
4
5
6
interface A { name: string, age: number } type B = A & { name : number } // B的name和A的name又交叉合并了一次,变成never了,当然如果name都是同一种类型合并后还是同一种类型。不同的字符串字面量也是一样的哦。

是不是达到和继承接口一样的效果。

交叉联合类型

复制代码
1
2
3
4
5
6
7
8
9
10
// 错误 type A = "a" | "A"; type B = "b" | "B"; type AB = A & B; let ab: AB = "a"; // 报错,A与B没有交集 type A = "a" | "A"; type B = "a" | "B"; type AB = A & B; // 交集为a,所以 AB类型为 'a' let ab: AB = "a"; // 正常赋值

当&和|同时使用时,&的优先级大于|。这里就不举例了。


类型缩减

例如:

复制代码
1
2
type BorderColor = 'black' | 'red' | 'green' | 'yellow' | 'blue' | string; // 前面的因为都属于string类型,最后面的又是个string类型,所以整体缩减成 string

这样,编辑器就不会提示BorderColor里有'black' | 'red' | 'green' | 'yellow' | 'blue'这些东西了。解决办法是类型后面加个符号:

复制代码
1
2
type BorderColor = 'black' | 'red' | 'green' | 'yellow' | 'blue' | string & {}; // 字面类型都被保留

其实这个还不是重要的,重要的是当联合类型的成员是接口类型,如果满足其中一个接口的属性是另外一个接口属性的子集,这个属性也会类型缩减!

复制代码
1
2
3
4
5
6
7
8
9
10
type A = | { age: "1"; } | { age: "1" | "2"; [key: string]: string; }; let a = { age: "2" }; // 此时age被缩减为"1" | "2"了,怕不怕

平时要注意这个问题。

最后

以上就是糟糕胡萝卜最近收集整理的关于【TS基础】个人学习记录6-零碎概念之类型推论、联合类型、类型断言、类型别名、字符串字面量、交叉类型类型推论联合类型类型断言类型别名字符串字面量交叉类型类型缩减的全部内容,更多相关【TS基础】个人学习记录6-零碎概念之类型推论、联合类型、类型断言、类型别名、字符串字面量、交叉类型类型推论联合类型类型断言类型别名字符串字面量交叉类型类型缩减内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(73)

评论列表共有 0 条评论

立即
投稿
返回
顶部