【问题标题】:Why can't TypeScript infer the return type of this function?为什么 TypeScript 不能推断这个函数的返回类型?
【发布时间】:2021-07-17 04:34:45
【问题描述】:

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

这是一个有点无用的版本的示例,它只是将对象的命名属性替换为其他可能属于不同类型的值。该代码有效 - 它用新值替换了命名属性 - 但由于某种原因,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!');
  }
});

【问题讨论】:

  • 实际上,我的回答可能会被误解。您是否尝试更改克隆中的属性类型?还是您要强制执行现有类型?
  • 试图改变类型。

标签: typescript typescript-generics


【解决方案1】:

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

如果没有明确的返回类型,我看不出如何解决这个问题,但如果添加返回类型,至少会推断出正确的类型。或许你可以对你的函数的其他版本做类似的事情。

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

TypeScript playground

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-05-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多