类型推断 -- Typescript基础篇(13)

2,477 阅读3分钟

我们并不总是需要为变量指定具体的类型,ts会根据一些规则推断变量类型。

基础类型推断

如果我们在定义一个基础类型时,只为其赋值,并未具体指定类型:

let a = 1; // 自动推断 a 为number类型
let b = "str"; // 自动推断 b 为string类型

如果不是用let关键字,而是用const,情况会有些不同:

const foo = 1; // foo类型为1
const bar = "str"; // bar类型为"str"
let baz = foo;  // baz类型为number

因为const 指定的变量表示是常量,不可变。所以ts能在基础类型的基础上进一步推断出更具体的类型,这个和字面量类型相似。而let指定的变量是可变类型,所以只能推断出基础类型。

如果一个类型的赋值加入了条件判断,如三元表达式,它会被推断为联合类型:

// fn是一个返回值为boolean的方法
let x = fn() ? "string" : fn() ? true : 1;
// x 类型为 string | number | boolean

// 如果使用 const
const x = fn() ? "string" : fn() ? true : 1;
// x 类型为 true | "st" | 1

枚举和类也是同样效果

数组类型

如果我们有以下数组变量arr

const arr = [1, "string", undefined];

此时ts会将它推断为(string | number | undefined)[]类型,因为该数组中包含了numberstringundefined等类型,所以ts会推断数组是由这几个类型的联合类型组成。

函数类型

如果我们指定了函数的类型,那么在给函数赋值时,就不在需要显式指定,ts会自动推断:

let fn: (a: number, b: number) => void;
// 此时a,b会自动被推断为number类型
fn = (a, b) => {
  a + b;
};

// 第二个参数为string类型,类型不兼容
fn(1,"st")

如果我们没有显式指定返回值类型,ts会根据参数类型和return语句推断:

// 返回结果会被推断为string类型
function callback(a: number, b: string) {
  return a + b;
}
// Type 'string' is not assignable to type 'number'
const result: number = callback(1, "s");

如果我们没有显式指定函数参数类型,会被默认推断为any:

// a为any,所以我们在函数内访问a的任意属性都是合法的
function anyFn(a) {
  a.name;
}

这是不安全的,我们希望尽量避免使用any,可以在配置文件中开启noImplicitAny。此时对于这种推断出为any的类型会报错:

implicit-any

但是显式指定参数为any是合法的。

结构化

如果我们声明一个不带类型的对象:

const data = {
  name: "xxx",
  age: 20,
};

// 此时的data会被推断为一个匿名类型
// {
//    name: string;
//    age: number;
// }

// type '1' is not assignable to type 'string'
data.name = 1;

如果我们想声明一个对象,并且对象的属性都是只读的,使用as const

const data = {
  name: "xxx",
  age: 20,
} as const;

// 此时的data会被推断为
// {
//    readonly name: "xxx";
//    readonly age: 20;
// }

就算此时有一个接口类型与之结构相同,data也不会被推断为该接口类型,可以理解为此时ts自动为data推断出一个匿名的接口类型。