为什么 TypeScript 无法推断出该函数的返回类型?

Jus*_*inM 4 typescript typescript-generics

我正在尝试编写一些克隆对象但更改深层属性的实用函数。

这是一个有点无用的版本的示例,它只是用可能属于不同类型的其他值替换对象的命名属性。该代码有效 - 它用新值替换了命名属性 - 但由于某种原因 TypeScript 无法确定所创建对象的类型并给出编译错误。为什么 TypeScript 不这样,我怎样才能让它工作?

我可以显式定义返回类型 - 但是对于该函数的版本来说,这会变得很尴尬,该版本会深度更改属性几个级别。

it('change type of input object', () => {
  const cloneWith = <T extends Record<string, unknown>, A extends keyof T, V>(
    i: T,
    a: A,
    value: V
  ) => ({
    ...i,
    [a]: value,
  });
  const source = { name: 'bob', age: 90 };
  const result = cloneWith(source, 'age', 'old!');

  // This test passes
  expect(result.age).to.equal('old!');

  // But compile error - number and string have no overlap -
  // on this line
  if (result.age === 'old!') {
    console.log('That person is old!');
  }
});
Run Code Online (Sandbox Code Playgroud)

Obl*_*sys 5

使用扩展语法创建的对象文字的推断类型并不总是理想的,特别是在涉及计算属性时。在这种情况下,T与索引签名相交{[x: string]: V},而Omit<T, A> & {[K in A]: V}更有意义(T没有属性和具有键和值A的单独对象的交集)。AT

我不知道如果没有显式返回类型,您将如何解决这个问题,但如果您添加返回类型,至少会推断出正确的类型。也许您可以对函数的其他版本执行类似的操作。

const cloneWith = <T, A extends keyof T, V>(
  i: T,
  a: A,
  value: V
): Omit<T, A> & {[K in A]: V} => ({
  ...i,
  [a]: value,
});

const source = { name: 'bob', age: 90 };
const result = cloneWith(source, 'age', 'old!');
type TestSource = typeof source.age // number
type TestResult = typeof result.age // string
Run Code Online (Sandbox Code Playgroud)

TypeScript 游乐场