logologo

TypeScript 4.9更新内容的学习

Jul 4, 2023 · 2 min

satisfies 操作符

ts 时,有时候我们希望表达式能与某个类型匹配,同时又能有准确的推断能力:

type Colors = 'red' | 'green' | 'blue'
type RGB = [red: number, green: number, blue: number]

const palette: Record<Colors, string | RGB> = {
  red: [255, 0, 0],
  green: '#00ff00',
  blue: [0, 0, 255],
}

如上 palette 的键值均有约束,但是对于每个属性值无法做出准确地推断:

// 期望是string类型,但会得到 string | RGB
palette.green

// 同理期望 期望RGB
palette.red

而如果我们去掉 Record<Colors, string | RGB> 后,上诉推断是可以生效了,但声明的时候就无法保证匹配想要的类型的:

const a = {
  // 不小心打错了,也不会报错
  belu: '#ffffff',
}

satisfies 可以完美地解决如上两个问题:

const palette = {
  red: [255, 0, 0],
  green: '#00ff00',
  bleu: [0, 0, 255],
//  ~~~~ bleu 是个非法的key
} satisfies Record<Colors, string | RGB>

// 是个数组
const redComponent = palette.red.at(0)
// 是个字符串
const greenNormalized = palette.green.toUpperCase()

in 操作符收窄类型

in 操作符可以用来判断某个对象是否有某个 key,而也可以用来收窄类型:

interface RGB {
  red: number
  green: number
  blue: number
}
interface HSV {
  hue: number
  saturation: number
  value: number
}
function setColor(color: RGB | HSV) {
  if ('hue' in color) {
    // 此时可以收窄到 HSV类型
  }
  // ...
}

再过去,但是对于哪些没有在类型中列出来的属性,则无法做收窄,会报错

function foo(package: unknown) {
  if (typeof package === 'object') {
    if ('name' in package && typeof package.name === 'string') {
      // error
      package.name
    }
  }
}

package 被收窄到 object,而 in 会严格收窄到实际有列出要检查属性的类型,因此还是 object

而在 4.9 版本,在做 in 判断时,如果检查的属性是什么再类型里列出来的,则会将类型收窄成 object & Record<'name', unknown>,然后判断 typeof package.name === 'string' 后,进一步收窄到 object & Record<'name', string>

同时对 in 还做了进一步检查,in 的左侧可分配的类型是 string | number | symbol,右侧分配给 object,避免意外检查原始类型