JavaScript的内置原始类型,在 TypeScript 中它们都有对应的类型注解
tsconst 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');
JavaScript 中,
null
: 这里没有值undefined
: 这里有值,但是个空值而在 TypeScript 中,null 与 undefined 类型都是有具体意义的类型
strictNullChecks
检查的情况下,会被视作其他类型的子类型,比如 string 类型会被认为包含了 null 与 undefined 类型tsconst tmp1: null = null;
const tmp2: undefined = undefined;
// 仅在关闭 strictNullChecks 时成立,下同
const tmp3: string = null;
const tmp4: string = undefined;
在 JavaScript 中,void
是一个运算符,可以将任何表达式的值转换为 undefined
。使用 void
运算符时,只需要在其后面跟上要被计算的表达式即可。
jsvoid 0; // 返回 undefined
void "hello"; // 返回 undefined
void (1 + 2); // 返回 undefined
通常,void 运算符用于创建一个返回 undefined 的函数,以避免特定上下文中出现副作用。
jsvoid function iife() {
console.log("Invoked!");
}();
TypeScript 与 JavaScript 中不同的是,这里的 void
用于描述
return
语句return
一个值的函数的返回值ts
// func1 与 func2 的返回值类型都会被隐式推导为 void
// 但在实际的代码执行中,func1 与 func2 的返回值均是 undefined
function func1() {}
function func2() {
return;
}
// func3 其返回值类型被推导为了 undefined
function func3() {
return undefined;
}
相关信息
你可以认为 void 表示一个空类型,而 null 与 undefined 都是一个具有意义的实际类型
TypeScript 中有两种方式来声明一个数组类型:
ts// 这两种方式是完全等价的
const arr1: string[] = [];
const arr2: Array<string> = [];
数组是我们在日常开发大量使用的数据结构,但在某些情况下,使用 元组(Tuple) 来代替数组要更加妥当
ts// 数组对越界访问不会报错
const arr3: string[] = ['lin', 'bu', 'du'];
console.log(arr3[599]);
// 元组是指定了长度的数组,在越界访问时会给出类型报错
const arr4: [string, string, string] = ['lin', 'bu', 'du'];
console.log(arr4[599]);
// 元组内部也可以声明多个与其位置强绑定的,不同类型的元素
const arr5: [string, number, boolean] = ['type', 599, true];
// 元组也支持了在某一个位置上的可选成员
const arr6: [string, number?, boolean?] = ['type'];
// ts4.0后,可以为元组中的元素打上类似属性的标记
const arr7: [name: string, age: number, male?: boolean] = ['linbudu', 599, true];
元组的最大作用就是能够很好的提示显式或者隐式的越界访问
数组与元组层面也有着只读的修饰
tsconst myArray: readonly number[] = [1, 2, 3];
const myArray: ReadonlyArray<number> = [1, 2, 3];
myArray[0] = 4; // 报错:Type 'readonly number[]' cannot be mutated.
myArray.push(4); // 报错:Property 'push' does not exist on type 'readonly number[]'.
tsconst myTuple: readonly [string, number] = ["hello", 123];
myTuple[0] = "world"; // 报错:无法分配到 "myTuple" ,因为它是只读的。
myTuple.push(456); // 报错:属性 'push' 在只读数组上不存在。
你只能将整个数组/元组标记为只读,而不能像对象那样标记某个属性为只读。
一旦被标记为只读,那这个只读数组/元组的类型上,将不再具有 push
、pop
等方法(即会修改原数组的方法),因此报错信息也将是类型 xxx 上不存在属性“push”
这种。
这一实现的本质是只读数组与只读元组的类型实际上变成了 ReadonlyArray,而不再是 Array。
在 TypeScript 中我们也需要特殊的类型标注来描述对象类型,即 interface
,可以理解为它代表了这个对象对外提供的接口结构
。
ts// 声明一个结构
interface IDescription {
name: string;
age: number;
male: boolean;
}
// 使用这个结构来作为一个对象的类型标注
const obj1: IDescription = {
name: 'linbudu',
age: 599,
male: true,
};
interface
的标注有以下限制作用:
obj1.other = 'xxx'
这样属性访问赋值的形式类似于元组可选,在接口结构中同样通过 ?
来标记一个属性为可选
tsinterface IDescription {
name: string;
age: number;
male?: boolean;
func?: Function;
}
// 即使你在 obj2 中定义了 male 属性,但当你访问 obj2.male 时
// 它的类型仍然会是 boolean | undefined
const obj2: IDescription = {
name: 'linbudu',
age: 599,
male: true,
// 无需实现 func 也是合法的
};
readonly
可以标记这个属性为只读
tsinterface IDescription {
readonly name: string;
age: number;
}
const obj3: IDescription = {
name: 'zhang san',
age: 599,
};
// 无法分配到 "name" ,因为它是只读属性
obj3.name = "silon";
用 typ
e(Type Alias,类型别名)也可以代替interface
结构描述对象。
tstype Person = {
name: string;
age: number;
email?: string; // 可选属性
};
const person1: Person = {
name: "Alice",
age: 30,
email: "alice@example.com"
};
const person2: Person = {
name: "Bob",
age: 25
};
更推荐的方式是:
interface
: 用来描述对象、类的结构type
:用来将一个函数签名、一组联合类型、一个工具类型等等抽离成一个完整独立的类型。大部分场景下interface
都可以被type
所取代,因此,只要你觉得统一使用类型别名让你觉得更整齐,也没什么问题。
原型链的顶端是 Object
以及 Function
,这也就意味着:
所有的原始类型与对象类型最终都指向 Object
,在 TypeScript 中就表现为 Object
包含了所有的类型
ts// 对于 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 = [];
和 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
在任何情况下,你都不应该使用这些装箱类型。
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 = [];
{}
表示一个合法的,但内部无属性定义的空对象。
它意味着任何非 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: {} = [];
虽然能够将其作为变量的类型,但你实际上无法对这个变量进行任何赋值操作
const tmp30: {} = { name: 'linbudu' }; tmp30.age = 18; // X 类型“{}”上不存在属性“age”。
在任何时候都不要,不要,不要使用 Object 以及类似的装箱类型
当你不确定某个变量的具体类型,但能确定它不是原始类型,可以使用 object。
要避免使用{}。{}意味着任何非 null / undefined 的值,从这个层面上看,使用它和使用 any 一样恶劣
本文作者:Silon汐冷
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!