Skip to content

类型拓宽

类型拓宽

类型拓宽:初始值字面量推断出来的类型

字面量类型拓宽

字面量类型拓宽条件

  1. 通过 let,var 定义的变量、函数形参、对象非只读属性,指定了初始值
  2. 未显式添加类型注解
ts
let str = 'this is string' // 类型是 string
let strFun = (str = 'this is string') => str // 类型是 (str?: string) => string;
const specifiedStr = 'this is string' // 类型是 'this is string'
let str2 = specifiedStr // 类型是 'string'
let strFun2 = (str = specifiedStr) => str // 类型是 (str?: string) => string;

添加类型注解限制类型拓宽

js
{
  const specifiedStr: 'this is string' = 'this is string' // 类型是 '"this is string"'
  let str2 = specifiedStr // 即便使用 let 定义,类型是 'this is string'
}

特定类型拓宽

通过 let、var 定义的变量如果满足未显式声明类型注解且被赋予了 null 或 undefined 值,则推断出这些变量的类型是 any

js
{
  let x = null // 类型拓宽成 any
  let y = undefined // 类型拓宽成 any

  /** -----分界线------- */
  const z = null // 类型是 null

  /** -----分界线------- */
  let anyFun = (param = null) => param // 形参类型是 null
  let z2 = z // 类型是 null
  let x2 = x // 类型是 null
  let y2 = y // 类型是 undefined
}

案例:定义了一个 Vector3 接口,然后定义了 getComponent 函数用于获取指定坐标轴的值

ts
interface Vector3 {
  x: number
  y: number
  z: number
}

function getComponent(vector: Vector3, axis: 'x' | 'y' | 'z') {
  return vector[axis]
}

问题:x 会被自动扩宽为 string

ts
let x = 'x'
let vec = { x: 10, y: 20, z: 30 }
// 类型“string”的参数不能赋给类型“"x" | "y" | "z"”的参数。
getComponent(vec, x) // Error

解决:使用 const 声明 x

js
const x = 'x' // type is "x"
let vec = { x: 10, y: 20, z: 30 }
getComponent(vec, x) // OK

对于任何给定的值都有许多可能的类型

js
const arr = ['x', 1]

/*
('x' | 1)[]
['x', 1]
[string, number]
readonly [string, number]
(string | number)[]
readonly (string | number)[]
[any, any]
any[]
*/

下面的例子中,变量 x 的类型被推断为字符串

js
let x = 'semlinker'
x = 'kakuqo'
x = 'lolo'

以下代码也合法,一般规则是,变量的类型在声明之后不应该改变,但 TypeScript 试图在特殊性和灵活性之间取得平衡。

js
let x = 'x'
x = /x|y|z/
x = ['x', 'y', 'z']

控制类型拓宽

非原始值类型拓宽

const:使用 const 会将值限制为 字面量

数组和对象类型拓宽

js
const obj = {
  x: 1
}

obj.x = 6 // ok
obj.x = '6' // Type 'string' is not assignable to type 'number'
obj.y = 8 // Property 'y' does not exist on type '{ x: number; }'

TypeScript 默认行为:通过属性初始化来推断属性类型

覆盖默认行为

通过提供显式注解,覆盖默认行为

js
// Type is { x: 1 | 3 | 5; }
const obj: { x: 1 | 3 | 5 } = {
  x: 1
}
const 断言

const 断言,ts 将它推断出最窄的类型,没有拓宽。

区别:不同于 let 和 const 中的 const,这里 const 是类型级构造,在值空间中引入符号。

js
// Type is { x: number; y: number; }
const obj1 = {
  x: 1,
  y: 2
};

// Type is { x: 1; y: number; }
const obj2 = {
  x: 1 as const,
  y: 2,
};

// Type is { readonly x: 1; readonly y: 2; }
const obj3 = {
  x: 1,
  y: 2
} as const;