TS中类型大小写的区别:如Number和number
// number是描述基础类型,Number是描述类的实例(类也可以当作类型)let num1: number = 1 // oklet num2: Number = 1 // ok,因为1是Number的实例(依据是可以调用Number上的方法)let num3: number = Number(1) // oklet num4: number = new Number(1) // error:不能将类型“Number”分配给类型“number”。let num5: Number = new Number(1) // oklet num6: Object = new Number(1) // ok
boolean
在 TypeScript 中,boolean 是 JavaScript 中的基本类型,而 Boolean 是 JavaScript 中的构造函数。
注意点:new Boolean() 返回的是一个 Boolean 对象不是布尔值
let createdByNewBoolean: boolean = new Boolean(1);// 不能将类型“Boolean”分配给类型“boolean”。“boolean”是基元,但“Boolean”是包装器对象。如可能首选使用“boolean”。
直接调用 Boolean 可以返回一个 boolean 类型:
let createdByBoolean: boolean = Boolean(1);
其他基本类型(除了 null 和 undefined)一样,不再赘述。
void
JavaScript 没有空值(Void)的概念,在 TypeScript 中,可以用 void 表示没有任何返回值,只能被赋值为 undefined 和 null(只在 strictNullChecks: false时有效):
let unusable: void = undefined;function getVoid(): void {return undefined}
null / undefined
与 void 的区别是,undefined 和 null 是所有类型的子类型。也就是说 undefined 类型的变量,在非严格模式下("strict": false)可以赋值给任何类型的变量,null在strictNullChecks: false情况下可以赋值给任何类型的变量
如果是可选值,那么赋值一个空对象 { }并不会报错。
但是如果是string | null,传入一个空对象,一般静态检查就会报错了。null通常用于对于某个参数需要它一定要存在的时候,即使可能在某种场景初始化的时候是空值。
never / unknown

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

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
never可以用来对联合类型做详细的检查,保证能够穷尽所有的联合类型:```typescriptinterface Square {kind: 'square';size: number;}interface Rectangle {kind: 'rectangle';width: number;height: number;}// 如果有人给Shape类型新增了 `Circle` 类型,希望 TypeScript 能在任何被需要的地方抛出错误interface Circle {kind: 'circle';radius: number;}type Shape = Square | Rectangle | Circle;function area(s: Shape) {if (s.kind === 'square') {return s.size * s.size;} else if (s.kind === 'rectangle') {return s.width * s.height;} else {// Error: 'Circle' 不能被赋值给 'never'const _exhaustiveCheck: never = s;return _exhaustiveCheck; // 在"strictNullChecks": true, 情况下必须要返回}}// 或者用switch实现function area(s: Shape) {switch (s.kind) {case 'square':return s.size * s.size;case 'rectangle':return s.width * s.height;case 'circle': // 强制添加新的条件,否则default将会报错return Math.PI * s.radius ** 2;default:const _exhaustiveCheck: never = s;return _exhaustiveCheck;}}
ts官方的工具类型中,获取参数类型的方法Paramters(传入的范型参数T不是函数类型的,就返回never,是函数类型就返回参数类型),抽取相交的属性Extract方法等,内部都有never的身影:
never 与 void 的差异:
- 没有显式返回值的函数会隐式返回
undefined,尽管这样的函数 “什么也不返回”,但实际上它是会返回的,尽管通常忽会略这种返回值,在 TypeScript 中这些函数的返回类型被推断为void never类型的函数永不返回,也不返回undefined,一般意味着该函数没有正常完成,可能会抛出异常或根本无法退出执行。- void 类型可以被赋值(在
strictNullChecking: false时),但是never除了 本身以外,其他任何类型不能赋值给 never。readonly
比如 react 中,props 和 state 都是 readonly的。因此,直接给它们赋值会报错,只能通过 setState这种对外暴露的方法操作。
注意事项,小写的 readonly 关键字只能标记普通属性比如字符串数字等,而首字母大写的 Readonly 加上泛型,则是用于标记整个对象数组等的转换。
字面量类型
```typescript const foo = ‘Hello World’;let foo: 'Hello';foo = 'Bar'; // Error: 'bar' 不能赋值给类型 'Hello'
// 使用一个捕获的类型 let bar: typeof foo;
// bar 仅能被赋值 ‘Hello World’ bar = ‘Hello World’; // ok bar = ‘anything else’; // Error
```typescripttype CardinalDirection = 'North' | 'East' | 'South' | 'West';function move(distance: number, direction: CardinalDirection) {// ...}move(1, 'North'); // okmove(1, 'Nurth'); // Error
类型别名
类型别名用来给一个类型起个新名字。类型别名常用于联合类型。
type Name = string;type NameResolver = () => string;type NameOrResolver = Name | NameResolver;function getName(n: NameOrResolver): Name {if (typeof n === 'string') {return n;} else {return n();}}
联合类型
联合类型(Union Types)(并集)使用 | 分隔每个类型,表示取值可以为多种类型中的一种。
联合类型的变量在被赋值的时候,会根据类型推断的规则推断出一个类型:
let myFavoriteNumber: string | number;myFavoriteNumber = 'seven';console.log(myFavoriteNumber.length); // 5myFavoriteNumber = 7;console.log(myFavoriteNumber.length); // 编译时报错:类型“number”上不存在属性“length”。
当 TypeScript 不确定一个联合类型的变量到底是哪个类型的时候,只能访问此联合类型的所有类型里共有的属性或方法
function getLength(something: string | number): number {return something.length;}//类型“string | number”上不存在属性“length”。类型“number”上不存在属性“length”。
交叉类型
交叉类型**&**(这里交集可以理解为涵盖所有属性)是将多个类型合并为一个类型,合并多个类型的过程中,如果出现某些类型存在相同的成员,但对应的类型又不一致,那么合并后该成员对应的类型变为never:
interface Person1 {name: string;}interface Person2 {age: number;}interface Person3 {age: string;}type test = Person1 & Person2type test2 = Person2 & Person3let p1: test = { // ok,合并了Person1、Person2两个类型name: '',age: 12,}let p2: test2 = { // 合并age类型时既是string类型又是number类型,所以最后变为neverage: 12, // error,不能将类型“number”分配给类型“never”}
与联合类型| (并集)的区别:
- 联合类型合并的时候不会将某些相同类型合并成never。 ```typescript interface Person1 { age: number; } interface Person2 { age: string; }
let p1: Person1 | Person2 = { // ok age: 12, }
let p2: Person1 | Person2 = { // ok age: ‘12’, }
- 交叉类型可以赋给没交叉之前的类型,但联合类型不行。即类型兼容,少的可以兼容(赋给)多的```typescriptinterface Person1 {age: number;}interface Person2 {name: string;}let p1: Person1 | Person2 = { // 联合类型age: 12,name: 'zhangsan'}let p2: Person1 & Person2 = { // 交叉类型age: 12,name: 'zhangsan'}let p3: Person1 // 没交叉之前的类型p3 = p1 // eror: 类型 "Person2" 中缺少属性 "age",但类型 "Person1" 中需要该属性。p3 = p2 // ok
类型推断
使用规则:
- 如果
TS能够自动分析出变量类型(即类型推断),就不需要再手动添加类型注解 - 如果
TS无法分析变量类型的话,就需要使用类型注解
TypeScript 会在没有明确的指定类型的时候推测出一个类型,这就是类型推断(Type Inference)。
// 不定义函数返回值的类型function add(one: number, two: number) {return one + two + '' // 不会报错,此时total: string}const total = add(1, 2)// neverfunction error(): never {throw new Error()console.log('hello') // never表示永远执行不到}function testWhile(): never {while(true) {}console.log('hello') // never表示永远执行不到,上面死循环}
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查:
let myFavoriteNumber;myFavoriteNumber = 'seven';myFavoriteNumber = 7;
