一、原始类型的类型标注
(1)除了最常见的 number / string / boolean / null / undefined, ECMAScript 2015(ES6)、2020 (ES11) 又分别引入了 2 个新的原始类型:symbol 与 bigint 。在 TypeScript 中它们都有对应的类型注解。
const name: string = 'linbudu';
const age: number = 24;
const male: boolean = false;
const undef: undefined = undefined;
const nul: null = null;
const obj: object = { name, age, male };
const bigintVar1: bigint = 9007199254740991n;
const bigintVar2: bigint = BigInt(9007199254740991);
const symbolVar: symbol = Symbol('unique');
(2) null 与 undefined
const tmp1: null = null;
const tmp2: undefined = undefined;
const tmp3: string = null; // 仅在关闭 strictNullChecks 时成立,下同
const tmp4: string = undefined;
1、在 JavaScript 中,null 与 undefined 分别表示“这里有值,但是个空值”和“这里没有值”。
2、而在 TypeScript 中,null 与 undefined 类型都是有具体意义的类型。也就是说,它们作为类型时,表示的是一个有意义的具体类型值。这两者在没有开启 strictNullChecks
检查的情况下,会被视作其他类型的子类型,比如 string 类型会被认为包含了 null 与 undefined 类型
(3)void
const voidVar1: void = undefined;
const voidVar2: void = null; // 需要关闭 strictNullChecks
1、TypeScript 的原始类型标注中也有 void,但与 JavaScript 中不同的是,这里的 void 用于描述一个内部没有 return 语句,或者没有显式 return 一个值的函数的返回值。
2、null 与 undefined可以赋值给void类型的变量。
二、数组类型标注
const arr1: string[] = [];
const arr2: Array<string> = [];
(1)元组(Tuple)
const arr4: [string, string, string] = ['lin', 'bu', 'du'];
console.log(arr4[599]);
1、在某些情况下,使用 元组(Tuple) 来代替数组要更加妥当,比如一个数组中只存放固定长度的变量,但我们进行了超出长度地访问。
2、支持在某一个位置上的可选成员。对于标记为可选的成员,在 --strictNullCheckes
配置下会被视为一个 string | undefined
的类型。
3、TypeScript 4.0 中,有了具名元组(Labeled Tuple Elements)的支持。
4、使用元组确实能帮助我们进一步提升数组结构的严谨性,包括基于位置的类型标注、避免出现越界访问等等。
5、数组与元组只读的修饰
你只能将整个数组/元组标记为只读,而不能像对象那样标记某个属性为只读。
一旦被标记为只读,那这个只读数组/元组的类型上,将不再具有 push、pop 等方法(即会修改原数组的方法),因此报错信息也将是类型 xxx 上不存在属性“push”这种。这一实现的本质是只读数组与只读元组的类型实际上变成了 ReadonlyArray,而不再是 Array。
三、对象的类型标注
interface IDescription {
name: string;
age: number;
male: boolean;
}
const obj1: IDescription = {
name: 'linbudu',
age: 599,
male: true,
};
每一个属性的值必须一一对应到接口的属性类型
不能有多的属性,也不能有少的属性,包括直接在对象内部声明,或是
obj1.other = 'xxx'
这样属性访问赋值的形式
(1)修饰接口属性
1、可选:?
interface IDescription {
name: string;
age: number;
male?: boolean;
func?: Function;
}
2、只读:readonly(作用:防止对象的属性被再次赋值)
interface IDescription {
readonly name: string;
age: number;
}
四、type 与 interface
(1)interface 用来描述对象、类的结构,而类型别名用来将一个函数签名、一组联合类型、一个工具类型等等抽离成一个完整独立的类型。
(2)大部分场景下接口结构都可以被类型别名所取代,因此,只要你觉得统一使用类型别名让你觉得更整齐,也没什么问题
五、object、Object 以及 { }
(1)Object
1、原型链的顶端是 Object 以及 Function,这也就意味着所有的原始类型与对象类型最终都指向 Object,在 TypeScript 中就表现为 Object 包含了所有的类型。
// 对于 undefined、null、void 0 ,需要关闭 strictNullChecks
const tmp1: Object = undefined;
const tmp2: Object = null;
const tmp3: Object = void 0;
const tmp4: Object = 'linbudu';
const tmp5: Object = 599;
const tmp6: Object = { name: 'linbudu' };
const tmp7: Object = () => {};
const tmp8: Object = [];
2、和 Object 类似的还有 Boolean、Number、String、Symbol,这几个装箱类型(Boxed Types) 同样包含了一些超出预期的类型。以 String 为例,它同样包括 undefined、null、void,以及代表的 拆箱类型(Unboxed Types) string,但并不包括其他装箱类型对应的拆箱类型,如 boolean 与 基本对象类型
const tmp9: String = undefined;
const tmp10: String = null;
const tmp11: String = void 0;
const tmp12: String = 'linbudu';
// 以下不成立,因为不是字符串类型的拆箱类型
const tmp13: String = 599; // X
const tmp14: String = { name: 'linbudu' }; // X
const tmp15: String = () => {}; // X
const tmp16: String = []; // X
3、在任何情况下,你都不应该使用这些装箱类型。
(2)object
1、object 的引入就是为了解决对 Object 类型的错误使用,它代表所有非原始类型的类型,即数组、对象与函数类型这些:
const tmp17: object = undefined;
const tmp18: object = null;
const tmp19: object = void 0;
const tmp20: object = 'linbudu'; // X 不成立,值为原始类型
const tmp21: object = 599; // X 不成立,值为原始类型
const tmp22: object = { name: 'linbudu' };
const tmp23: object = () => {};
const tmp24: object = [];
(3){}
1、可以认为{}
就是一个对象字面量类型(对应到字符串字面量类型这样)。否则,可以认为使用{}
作为类型签名就是一个合法的,但内部无属性定义的空对象,这类似于 Object(想想 new Object()
),它意味着任何非 null / undefined 的值。
const tmp25: {} = undefined; // 仅在关闭 strictNullChecks 时成立,下同
const tmp26: {} = null;
const tmp27: {} = void 0; // void 0 等价于 undefined
const tmp28: {} = 'linbudu';
const tmp29: {} = 599;
const tmp30: {} = { name: 'linbudu' };
const tmp31: {} = () => {};
const tmp32: {} = [];
2、虽然能够将其作为变量的类型,但你实际上无法对这个变量进行任何赋值操作:
const tmp30: {} = { name: 'linbudu' };
tmp30.age = 18; // X 类型“{}”上不存在属性“age”。
(4)总结
在任何时候都不要,不要,不要使用 Object 以及类似的装箱类型。
当你不确定某个变量的具体类型,但能确定它不是原始类型,可以使用 object。但我更推荐进一步区分,也就是使用
Record<string, unknown>
或Record<string, any>
表示对象,unknown[]
或any[]
表示数组,(...args: any[]) => any
表示函数这样。我们同样要避免使用
{}
。{}
意味着任何非null / undefined
的值,从这个层面上看,使用它和使用any
一样恶劣。