大部分的ts都由类型推论缘起,类型兼容性引发,类型保护/断言解决,any不是解决问题的方式,这篇文章才是。
帮助你更深的理解ts类型的定义和使用,解决ts类型报错问题,和anystyle说再见。
那些你未定义的类型:类型推论
推断发生在初始化变量和成员,设置默认参数值和决定函数返回值时。
let x = 3;
// 变量x的类型被推断为数字。
当需要从几个表达式中推断类型时候,会使用这些表达式的类型(候选类型)来推断出一个最合适的通用类型。当候选类型不能使用的时候我们需要明确的指出类型。
let zoo: Animal[] = [new Rhino(), new Elephant(), new Snake()];
类型推断的结果可能为联合数组类型
(Rhino | Elephant | Snake)[]
按上下文归类会发生在表达式的类型与所处的位置相关时。 通常包含函数的参数,赋值表达式的右边,类型断言,对象成员和数组字面量和返回值语句。 上下文类型也会做为最佳通用类型的候选类型。
ts如何进行类型对比:类型兼容性
TypeScript里的类型兼容性是基于结构子类型的,即定义了不同接口但相同结构的类型兼容。
在使用基于名义类型的语言,比如C#或Java中,定义了不同接口的类型,无论其实际结构是否相同,都报错。
对象赋值的对与错:对象类型兼容
要检查y
是否能赋值给x
,编译器检查x
中的每个属性,看是否能在y
中也找到对应属性。而y中多余的属性不会引发错误。
函数间的赋值要求:函数兼容
要查看x
是否能赋值给y
,首先看它们的参数列表。 x
的每个参数必须能在y
里找到对应类型的参数。 注意的是参数的名字相同与否无所谓,只看它们的类型。
类型系统强制源函数的返回值类型必须是目标函数返回值类型的子类型。
总之,右侧附给左侧,要求右侧含有左侧。
当一个函数有剩余参数时,它被当做无限个可选参数。
function invokeLater(args: any[], callback: (...args: any[]) => void) {
/* ... Invoke callback with 'args' ... */
}
// Unsound - invokeLater "might" provide any number of arguments
invokeLater([1, 2], (x, y) => console.log(x + ', ' + y));
// Confusing (x and y are actually required) and undiscoverable
invokeLater([1, 2], (x?, y?) => console.log(x + ', ' + y));
更灵活的定义类型:高级类型
更灵活的定义类型,定义多个类型使用修饰符连接,可定义更加广泛的类型
交叉类型是将多个类型合并为一个类型。例如, Person & Serializable & Loggable
同时是 Person
和 Serializable
和 Loggable
。就是说这个类型的对象同时拥有了这三种类型的成员。
联合类型表示一个值可以是几种类型之一。 我们用竖线( |
)分隔每个类型,所以 number | string | boolean
表示一个值可以是 number
, string
,或 boolean
。如果一个值是联合类型,我们只能访问此联合类型的所有类型里共有的成员。(指两个接口中的共有属性)
更精妙的类型断言:类型保护
即显示的对变量类型进行判断,被判断或的类型ts会将对应的类型结果附给变量,该判断可通过自定义函数,也可通过JS自带的typeof等函数,但是注意ts只会附魔固定的类型,而不是typeof检测出的所有类型。
什么类型我说了算:自定义类型保护
更灵活的类型断言,书写含有特定返回值类型的函数,该返回值帮助ts做出类型断言
function isFish(pet: Fish | Bird): pet is Fish {
return (<Fish>pet).swim !== undefined;
}
使用类型断言使联合类型中,非共有成员的使用合法,返回值使用pet is Fish这中格式的类型谓词指定,使用该函数会使得变量缩减为某个具体的类型,只要这个类型与变量的原始类型是兼容的。
// 'swim' 和 'fly' 调用都没有问题了
if (isFish(pet)) {
pet.swim();
}
else {
pet.fly();
}
TypeScript不仅知道在 if
分支里 pet
是 Fish
类型; 它还清楚在 else
分支里,一定 不是 Fish
类型,一定是 Bird
类型。
自动确认类型:typeof
类型保护
function padLeft(value: string, padding: string | number) {
if (typeof padding === "number") {
return Array(padding + 1).join(" ") + value;
}
if (typeof padding === "string") {
return padding + value;
}
throw new Error(`Expected string or number, got '${padding}'.`);
}
只有两种形式能被识别: typeof v === "typename"
和 typeof v !== "typename"
, "typename"
必须是 "number"
, "string"
, "boolean"
或 "symbol"
。
自动确认类型:instanceof
类型保护
instanceof
类型保护是通过构造函数来细化类型的一种方式。
instanceof
的右侧要求是一个构造函数,TypeScript将细化为:
- 此构造函数的
prototype
属性的类型,如果它的类型不为any
的话 - 构造签名所返回的类型的联合
null与其他类型间的赋值:可以为null的类型
默认情况下,类型检查器认为 null
与 undefined
可以赋值给任何类型。
--strictNullChecks
标记可以解决此错误
:当你声明一个变量时,它不会自动地包含 null
或 undefined
。 你可以使用联合类型明确的包含它们。
即使使用了此标记,可选参数与可选属性任然可以赋值为undefined
关于null的报错解决:为null添加类型保护
if (sn == null)
return sn || "default";
如果编译器不能够去除 null
或 undefined
,你可以使用类型断言手动去除。 语法是添加 !
后缀: identifier!
从 identifier
的类型里去除了 null
和 undefined
。
给定一个变量name,其联合类型中包括null
name.charAt(0); // error, 'name' is possibly null
name!.charAt(0); // ok