根据其他参数推断参数类型和返回类型

Pau*_*non 6 typescript

函数接受一个对象,该对象可以是 TypeA 或 TypeB。第一个参数指定对象的类型。返回的类型还取决于第一个参数。

问题:TypeScript 无法推断 case 内(或 if 内)对象的类型。以下代码有错误。

我知道我可以通过明确类型(add as TypeA)来解决这个问题,但我想知道是否有其他解决方案来避免必须进行显式强制转换。

type TypeA = {
  input: { foo: string }
  output: { foo: number }
}

type TypeB = {
  input: { bar: string }
  output: { bar: number }
}

type Types = {
  TypeA: TypeA
  TypeB: TypeB
}

type TypesName = keyof Types

/**
 * Takes TypeA or TypeB objects, and the return type is based on the type.
 */
function transform<N extends TypesName>(typeName: N, typeValue: Types[N]['input']): Types[N]['output'] {
  switch (typeName) {
    case 'TypeA':
      return transformTypeA(typeValue) // Argument of type '{ foo: string; } | { bar: string; }' is not assignable to parameter of type '{ foo: string; }'.
    case 'TypeB':
      return transformTypeB(typeValue) // Argument of type '{ foo: string; } | { bar: string; }' is not assignable to parameter of type '{ bar: string; }'.
  }
  throw new Error('Unknown type')
}

function transformTypeA(typeA: TypeA['input']): TypeA['output'] {
  return { foo: parseInt(typeA.foo) }
}

function transformTypeB(typeB: TypeB['input']): TypeB['output'] {
  return { bar: parseInt(typeB.bar) }
}


const transformedValue = transform('TypeA', { foo: 'lol' })
console.log(transformedValue) // transformedValue is of type { foo: number }
Run Code Online (Sandbox Code Playgroud)

Tit*_*mir 3

有一种方法,但现在可能不值得。

type Union<N extends keyof Types = keyof Types> = N extends N ? [typeName: N, typeValue: Types[N]['input']]: never;

function transform<N extends TypesName>(...p: Union<N>): Types[N]['output'] {
  switch (p[0]) {
    case 'TypeA':
      return transformTypeA(p[1])
    case 'TypeB':
      return transformTypeB(p[1])
  }
}

Run Code Online (Sandbox Code Playgroud)

游乐场链接

我们需要使用索引访问,而不是定义单独的参数或解构参数,因为这是打字稿看到两个值相关的唯一方法。稍后可能会支持此功能,如此处所述

特别是,不支持将判别属性和有效负载属性解构为两个局部变量并期望两者之间耦合的模式,因为控制流分析器无法“看到”连接。例如:

type Data = { kind: 'str', payload: string } | { kind: 'num', payload: number };

function foo({ kind, payload }: Data) {
    if (kind === 'str') {
        payload.length;  // Error, payload not narrowed to string
    }
}
Run Code Online (Sandbox Code Playgroud)

我们稍后可能会支持这种模式,但在本次 PR 中可能不会。