TS中类型大小写的区别:如Number和number

  1. // number是描述基础类型,Number是描述类的实例(类也可以当作类型)
  2. let num1: number = 1 // ok
  3. let num2: Number = 1 // ok,因为1是Number的实例(依据是可以调用Number上的方法)
  4. let num3: number = Number(1) // ok
  5. let num4: number = new Number(1) // error:不能将类型“Number”分配给类型“number”。
  6. let num5: Number = new Number(1) // ok
  7. let num6: Object = new Number(1) // ok

boolean

在 TypeScript 中,boolean 是 JavaScript 中的基本类型,而 Boolean 是 JavaScript 中的构造函数。
注意点:new Boolean() 返回的是一个 Boolean 对象不是布尔值

  1. let createdByNewBoolean: boolean = new Boolean(1);
  2. // 不能将类型“Boolean”分配给类型“boolean”。“boolean”是基元,但“Boolean”是包装器对象。如可能首选使用“boolean”。

直接调用 Boolean 可以返回一个 boolean 类型:

  1. let createdByBoolean: boolean = Boolean(1);

其他基本类型(除了 null 和 undefined)一样,不再赘述。

void

JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void 表示没有任何返回值,只能被赋值为 undefined 和 null(只在 strictNullChecks: false时有效):

  1. let unusable: void = undefined;
  2. function getVoid(): void {
  3. return undefined
  4. }

null / undefined

与 void 的区别是,undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,在非严格模式下("strict": false)可以赋值给任何类型的变量,null在strictNullChecks: false情况下可以赋值给任何类型的变量
基础类型 - 图1
如果是可选值,那么赋值一个空对象 { }并不会报错。
但是如果是string | null,传入一个空对象,一般静态检查就会报错了。null通常用于对于某个参数需要它一定要存在的时候,即使可能在某种场景初始化的时候是空值。

never / unknown

基础类型 - 图2
unknown的使用

  • unknown是某些值的集合,任何值都能冠以类型unknown。这就是为什么unknown被称为顶端类型。
  • unknown和其它类型的联合类型都是unknownunknown和其它类型的交叉类型都是其它类型(一切都是为了安全)
  • 但是要将unknown类型赋值给其它类型,要先进行类型判断或断言后才能赋值。
  • 假如一个函数,会有很多类型的操作(包含字符串、数组的操作),这时候就可以把参数类型定义为unknown,通过unknown来避免代码中可能漏校验的情况。

基础类型 - 图3
never的使用

  • **never**用于永远不可能发生的情况,主要场景用于错误检查或收敛条件类型,属于底层类型。
  • **never**类型是任何类型的子类型,可以赋值给任何类型
  • **never**类型仅能被赋值给另外一个 **never**,不能赋值给any

它一般用于:

  • 一个从来不会有返回值的函数(如:如果函数内含有 while(true) {});
  • 一个总是会抛出错误的函数(如:function foo() { throw new Error('Not Implemented') },foo 的返回类型是 never) ```typescript function throwError(): never { throw new Error(‘Error!’); }

let x: string = throwError(); // OK:never类型是任何类型的子类型,可以赋值给任何类型 let err: never = x; // error:never类型仅能被赋值给另外一个 never let err2: never = throwError() // ok

  1. never可以用来对联合类型做详细的检查,保证能够穷尽所有的联合类型:
  2. ```typescript
  3. interface Square {
  4. kind: 'square';
  5. size: number;
  6. }
  7. interface Rectangle {
  8. kind: 'rectangle';
  9. width: number;
  10. height: number;
  11. }
  12. // 如果有人给Shape类型新增了 `Circle` 类型,希望 TypeScript 能在任何被需要的地方抛出错误
  13. interface Circle {
  14. kind: 'circle';
  15. radius: number;
  16. }
  17. type Shape = Square | Rectangle | Circle;
  18. function area(s: Shape) {
  19. if (s.kind === 'square') {
  20. return s.size * s.size;
  21. } else if (s.kind === 'rectangle') {
  22. return s.width * s.height;
  23. } else {
  24. // Error: 'Circle' 不能被赋值给 'never'
  25. const _exhaustiveCheck: never = s;
  26. return _exhaustiveCheck; // 在"strictNullChecks": true, 情况下必须要返回
  27. }
  28. }
  29. // 或者用switch实现
  30. function area(s: Shape) {
  31. switch (s.kind) {
  32. case 'square':
  33. return s.size * s.size;
  34. case 'rectangle':
  35. return s.width * s.height;
  36. case 'circle': // 强制添加新的条件,否则default将会报错
  37. return Math.PI * s.radius ** 2;
  38. default:
  39. const _exhaustiveCheck: never = s;
  40. return _exhaustiveCheck;
  41. }
  42. }

ts官方的工具类型中,获取参数类型的方法Paramters(传入的范型参数T不是函数类型的,就返回never,是函数类型就返回参数类型),抽取相交的属性Extract方法等,内部都有never的身影:
基础类型 - 图4
never 与 void 的差异:

  • 没有显式返回值的函数会隐式返回undefined,尽管这样的函数 “什么也不返回”,但实际上它是会返回的,尽管通常忽会略这种返回值,在 TypeScript 中这些函数的返回类型被推断为void
  • never类型的函数永不返回,也不返回undefined,一般意味着该函数没有正常完成,可能会抛出异常或根本无法退出执行。
  • void 类型可以被赋值(在strictNullChecking: false时),但是never除了 本身以外,其他任何类型不能赋值给 never。

    readonly

    比如 react 中,props 和 state 都是 readonly的。因此,直接给它们赋值会报错,只能通过 setState这种对外暴露的方法操作。
    基础类型 - 图5
    注意事项,小写的 readonly 关键字只能标记普通属性比如字符串数字等,而首字母大写的 Readonly 加上泛型,则是用于标记整个对象数组等的转换。
    基础类型 - 图6

    字面量类型

    1. let foo: 'Hello';
    2. foo = 'Bar'; // Error: 'bar' 不能赋值给类型 'Hello'
    ```typescript const foo = ‘Hello World’;

// 使用一个捕获的类型 let bar: typeof foo;

// bar 仅能被赋值 ‘Hello World’ bar = ‘Hello World’; // ok bar = ‘anything else’; // Error

  1. ```typescript
  2. type CardinalDirection = 'North' | 'East' | 'South' | 'West';
  3. function move(distance: number, direction: CardinalDirection) {
  4. // ...
  5. }
  6. move(1, 'North'); // ok
  7. move(1, 'Nurth'); // Error

类型别名

类型别名用来给一个类型起个新名字。类型别名常用于联合类型。

  1. type Name = string;
  2. type NameResolver = () => string;
  3. type NameOrResolver = Name | NameResolver;
  4. function getName(n: NameOrResolver): Name {
  5. if (typeof n === 'string') {
  6. return n;
  7. } else {
  8. return n();
  9. }
  10. }

联合类型

联合类型(Union Types)(并集)使用 | 分隔每个类型,表示取值可以为多种类型中的一种。
联合类型的变量在被赋值的时候,会根据类型推断的规则推断出一个类型:

  1. let myFavoriteNumber: string | number;
  2. myFavoriteNumber = 'seven';
  3. console.log(myFavoriteNumber.length); // 5
  4. myFavoriteNumber = 7;
  5. console.log(myFavoriteNumber.length); // 编译时报错:类型“number”上不存在属性“length”。

当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,只能访问此联合类型的所有类型里共有的属性或方法

  1. function getLength(something: string | number): number {
  2. return something.length;
  3. }
  4. //类型“string | number”上不存在属性“length”。类型“number”上不存在属性“length”。

交叉类型

交叉类型**&**(这里交集可以理解为涵盖所有属性)是将多个类型合并为一个类型,合并多个类型的过程中,如果出现某些类型存在相同的成员,但对应的类型又不一致,那么合并后该成员对应的类型变为never:

  1. interface Person1 {
  2. name: string;
  3. }
  4. interface Person2 {
  5. age: number;
  6. }
  7. interface Person3 {
  8. age: string;
  9. }
  10. type test = Person1 & Person2
  11. type test2 = Person2 & Person3
  12. let p1: test = { // ok,合并了Person1、Person2两个类型
  13. name: '',
  14. age: 12,
  15. }
  16. let p2: test2 = { // 合并age类型时既是string类型又是number类型,所以最后变为never
  17. age: 12, // error,不能将类型“number”分配给类型“never”
  18. }

与联合类型| (并集)的区别:

  • 联合类型合并的时候不会将某些相同类型合并成never。 ```typescript interface Person1 { age: number; } interface Person2 { age: string; }

let p1: Person1 | Person2 = { // ok age: 12, }

let p2: Person1 | Person2 = { // ok age: ‘12’, }

  1. - 交叉类型可以赋给没交叉之前的类型,但联合类型不行。即类型兼容,少的可以兼容(赋给)多的
  2. ```typescript
  3. interface Person1 {
  4. age: number;
  5. }
  6. interface Person2 {
  7. name: string;
  8. }
  9. let p1: Person1 | Person2 = { // 联合类型
  10. age: 12,
  11. name: 'zhangsan'
  12. }
  13. let p2: Person1 & Person2 = { // 交叉类型
  14. age: 12,
  15. name: 'zhangsan'
  16. }
  17. let p3: Person1 // 没交叉之前的类型
  18. p3 = p1 // eror: 类型 "Person2" 中缺少属性 "age",但类型 "Person1" 中需要该属性。
  19. p3 = p2 // ok

类型推断

使用规则:

  • 如果TS能够自动分析出变量类型(即类型推断),就不需要再手动添加类型注解
  • 如果TS无法分析变量类型的话,就需要使用类型注解

TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推断(Type Inference)。

  1. // 不定义函数返回值的类型
  2. function add(one: number, two: number) {
  3. return one + two + '' // 不会报错,此时total: string
  4. }
  5. const total = add(1, 2)
  6. // never
  7. function error(): never {
  8. throw new Error()
  9. console.log('hello') // never表示永远执行不到
  10. }
  11. function testWhile(): never {
  12. while(true) {}
  13. console.log('hello') // never表示永远执行不到,上面死循环
  14. }

如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查

  1. let myFavoriteNumber;
  2. myFavoriteNumber = 'seven';
  3. myFavoriteNumber = 7;