类型推断与断言
类型推断
在很多情况下,TypeScript 会根据上下文环境自动推断出变量的类型,无须我们再写明类型注解。因此,上面的示例可以简化为如下所示内容:
{
let str = 'this is string'; // 等价 let str: string = 'this is string'; 下面类似
let num = 1; // 等价
let bool = true; // 等价
}
{
const str = 'this is string'; // 不等价
const num = 1; // 不等价
const bool = true; // 不等价
}
我们把 TypeScript 这种基于赋值表达式推断类型的能力称之为类型推断。
在 TypeScript 中,具有初始化值的变量、有默认值的函数参数、函数返回的类型都可以根据上下文推断出来。比如我们能根据 return 语句推断函数返回的类型,如下代码所示:
{
/** 根据参数的类型,推断出返回值的类型也是 number */
function add1(a: number, b: number) {
return a + b
}
const x1 = add1(1, 1) // 推断出 x1 的类型也是 number
/** 推断参数 b 的类型是数字或者 undefined,返回值的类型也是数字 */
function add2(a: number, b = 1) {
return a + b
}
const x2 = add2(1)
const x3 = add2(1, '1') // ts(2345) Argument of type "1" is not assignable to parameter of type 'number | undefined
}
如果定义的时候没有赋值,不管之后有没有赋值,都会被推断成 any 类型而完全不被类型检查:
let myFavoriteNumber;
myFavoriteNumber = 'seven';
myFavoriteNumber = 7;
类型断言
有时候你会遇到这样的情况,你会比 TypeScript 更了解某个值的详细信息。通常这会发生在你清楚地知道一个实体具有比它现有类型更确切的类型。
通过类型断言这种方式可以告诉编译器,“相信我,我知道自己在干什么”。类型断言好比其他语言里的类型转换,但是不进行特殊的数据检查和解构。它没有运行时的影响,只是在编译阶段起作用。
TypeScript 类型检测无法做到绝对智能,毕竟程序不能像人一样思考。有时会碰到我们比 TypeScript 更清楚实际类型的情况,比如下面的例子:
const arrayNumber: number[] = [1, 2, 3, 4];
const greaterThan2: number = arrayNumber.find(num => num > 2); // 提示 ts(2322)
其中,greaterThan2 一定是一个数字(确切地讲是 3),因为 arrayNumber 中明显有大于 2 的成员,但静态类型对运行时的逻辑无能为力。
在 TypeScript 看来,greaterThan2 的类型既可能是数字,也可能是 undefined,所以上面的示例中提示了一个 ts(2322) 错误,此时我们不能把类型 undefined 分配给类型 number。
不过,我们可以使用一种笃定的方式——类型断言(类似仅作用在类型层面的强制类型转换)告诉 TypeScript 按照我们的方式做类型检查。
比如,我们可以使用 as 语法做类型断言,如下代码所示:
const arrayNumber: number[] = [1, 2, 3, 4];
const greaterThan2: number = arrayNumber.find(num => num > 2) as number;
语法
“尖括号”语法
let someValue: any = 'this is a string'
let strLength: number = (<string>someValue).length
as 语法
let someValue: any = 'this is a string'
let strLength: number = (someValue as string).length
非空断言
在上下文中当类型检查器无法断定类型时,一个新的后缀表达式操作符 ! 可以用于断言操作对象是非 null 和非 undefined 类型。
let mayNullOrUndefinedOrString: null | undefined | string;
mayNullOrUndefinedOrString!.toString(); // ok
mayNullOrUndefinedOrString.toString(); // ts(2531)
确定赋值断言
允许在实例属性和变量声明后面放置一个 ! 号,从而告诉 TypeScript 该属性会被明确地赋值。为了更好地理解它的作用,我们来看个具体的例子:
let x: number;
initialize();
// Variable 'x' is used before being assigned.(2454)
console.log(2 * x); // Error
function initialize() {
x = 10;
}
很明显该异常信息是说变量 x 在赋值前被使用了,要解决该问题,我们可以使用确定赋值断言:
let x!: number;
initialize();
console.log(2 * x); // Ok
function initialize() {
x = 10;
}