Other Types
any
any是一种使TypeScript代码退回到JavaScript代码编写的方案。它是TypeScript中一种特殊的类型,如果不希望特定的值导致类型检查错误,就可以使用any。当一个值是any类型,你可以访问它的任何属性,像一个函数调用它,给它赋值或者几乎任何其他语法合法的值。
由于any类型可以用于表示任意类型的值,因此主要用来描述不确定类型的变量。当你不想为了让TypeScript相信某行特定代码没问题而写出长的类型推断时,any类型很有用。
let obj: any = { x: 0 };
obj.bar = 100;
obj = 'hello';
const n: number = obj;
console.log(n);
any是其他类型的祖先,如果没有明确一个类型,并且TypeScript不能从上下文推断其类型,编译器将默认其类型是any。不过,通常情况下应避免这种情况,因为any类型不会被类型检查。可以使用noImplictAny编译选项标记任何隐式地any类型被视为一个错误,以减少程序出错的可能性。
由于使用any类型会破坏TypeScript对类型的检查,因此应尽量避免使用它。如果必须使用any类型,应尽量将它限制在一个小的范围内,并且尽量避免在其上执行不安全的操作,以避免类型错误和运行时错误。
let obj:any = {}
// runtime error: TypeError: obj.a is not a function
console.log(obj.a());
any类型可以赋值给任何类型
let a : number;
let b: any = 'abc';
a = b
- 可以访问任何属性
let obj: any = {
a: 1
}
console.log(obj.b);
- 可进行属性的追加
let obj: any = {
a: 1
}
obj.b = 2
- 其它类型可赋值给
any类型
let a;
let b = 1;
a = b
- 隐式
any在严格模式下是不被允许的
function plus(a, b) {
return a + b
}
unknown
unknown是TypeScript 3.0版本新增的一个顶级类型。unknown是any的类型安全对应类型。
其它类型可以分配给unknown,但是unknown不能分配给其它类型,除了本身和没有类型断言或基于类型窄化的控制流的any类型。
在使用时,如果不首先断言或窄化到更具体的类型,则不允许进行任何操作。
let myVar: unknown = 10;
let strVar: string = 'hello';
// 其它类型赋值给unknown类型
myVar = strVar;
// Type 'unknown' is not assignable to type 'string'.
strVar = myVar; // error
function test1(a: any) {
a.b();
}
function test2(a: unknown) {
// a' is of type 'unknown'.
a.b();
}
相比于any,编译器不会对unknown 类型跳过类型检查,因此更安全。使用之前,必须先检查其值的类型:
function test(a: unknown) {
if (typeof a === 'function') {
a();
} else if (typeof a === 'string') {
console.log(a.length);
} else {
// handle other cases
}
}
如果不知道一个值的类型,推荐使用unknown而不是any。
any vs unknown
| 特征 | any | unknown |
|---|---|---|
| 用法 | 变量、参数、返回值 | 变量、参数、返回值 |
| 类型检查 | 不进行 | 进行 |
| 可能的值 | 任何类型 | 未知类型,不能操作或使用未经验证的值 |
| 是否可赋值 | any与其它类型可相互赋值 | 其它类型可以赋值给unknown,反之则不行 |
| 安全性 | 低, 可能导致运行时错误 | 高, 不能直接使用, 要进行类型检查 |
| 隐式转换 | 支持 | 不支持 |
| 显示转换 | 可以显式断言缩小其类型 | 必须显式类型断言确定其类型 |
| 可读性 | 差 | 好 |
| 维护性 | 差 | 好 |
| 缺点 | 可能导致运行时错误, 不符合类型安全的最佳实践 | 显式类型转换会增加代码复杂度, 并且必须小心处理未知的值和类型推断 |
| 使用时的建议 | 避免, 尽量不使用 | 推荐 |
never
never是TypeScript的底层类型,表示没有可分配的类型了,永远不会出现的类型。
never类型是每个类型的子类型,并且可以赋值给每个类型。相反,没有类型是never的子类型,并且其他任何类型不能赋值给never类型,除了never本身以外。
当TypeScript推断出一个变量的类型是never时,通常意味着该变量可用作断言,会防止编译任何它可能有值。
一般情况下,不应该手动将变量声明为never类型,而应该通过类型推断得出。
// 函数由于抛出错误, 无法正常结束返回
function error(message: string): never {
throw new Error(message);
}
error('Something went wrong');
// 死循环, 无法正常结束返回
function infiniteLoop(): never {
while (true) {
// something
}
}
never的使用场景:
- 当函数抛出一个异常或无法执行到最后一行代码时,返回值类型可声明为
never。(如上案例) - 当被任何永远不可能为真的类型保护窄化时,变量会获得
never类型。
let strOrNum: string | number = 'hello';
if (typeof strOrNum === 'string') {
console.log(strOrNum.length);
} else if (typeof strOrNum === 'number') {
console.log(Number(strOrNum).toFixed(2));
} else {
// 类型被缩小为 never 类型,因为在之前的类型保护中已经排除了所有可能的类型
const unreachable: never = strOrNum;
}
object
要定义对象类型,只需列出其属性及其类型。
function printCoord(pt: { x: number; y: number }) {
console.log("The coordinate's x value is " + pt.x);
console.log("The coordinate's y value is " + pt.y);
}
printCoord({ x: 3, y: 7 });
声明对象类型的方式:
// [key: type]:type
const obj1: { [key: string]: number } = { foo: 1, bar: 2 };
console.log(obj1);
const obj2: object = { foo: 1, bar: 2 };
console.log(obj2);
// 声明为interface
interface MyObj {
foo: number;
bar: number;
}
const obj3: MyObj = { foo: 1, bar: 2 };
console.log(obj3);
在TypeScript,object类型用来描述任何非基本类型的数据,不包括原始类型string、number、boolean、symbol、null以及undefined ,包括函数、数组、Map等。一般来说,可以说object代表了所有的引用类型。
由于object类型表示具有任意属性的值,因此object类型可以作为所有对象的基础类型。如果any是所有类型的祖先,那么object可看做对象类型的祖先。
将一个变量声明为object类型时,仅仅表示一个具有任意属性的对象,且无法对这个对象的属性进行进一步的推断及访问,因此使用时可能会受到类型检查。如果需要更加准确的类型检查,可以使用具有特定属性及类型的类型别名type来表示对象类型。