对象文字具有函数时的打字稿错误

时间:2021-05-03 17:52:07

标签: typescript

代码如下:

function id<T>(x: T) {
    return x
}

const keys: string[] = []

const obj1 = Object.fromEntries(keys.map(k => [
    k, 
    {test() {}} // OK
]))

const obj2 = Object.fromEntries(keys.map(k => [
    k, 
    id({test() {}}) // Error
]))

const obj3 = Object.fromEntries(keys.map(k => [
    k, 
    id({test: new Date()}) // OK
]))

我不知道为什么打字稿会出现这个错误:

Argument of type '{ test(): void; }' is not assignable to parameter of type 'T'.
  'T' could be instantiated with an arbitrary type which could be unrelated to '{ test(): void; }'.

似乎只有对象字面量中的函数会导致此错误。

此处的 id 函数仅用于演示。实际函数具有此签名:

<T>(x: T) => T & SomeExtraFields

打字稿版本:4.2.3

2 个答案:

答案 0 :(得分:1)

您有多种处理方法:

首先:

如果你使用引用而不是文字类型,它会按预期工作:

const id = <T,>(x: T): T => x

const keys: string[] = []

const iter = keys.map(k => [
    k,
    id({ test() { } }) // ok
])

const obj2 = Object.fromEntries(iter)

或:

const idResult = id({ test() { } })
const obj2 = Object.fromEntries(keys.map(k => [
    k,
    idResult
]))

第二

让我们尝试使用箭头函数作为属性而不是方法:

const obj2 = Object.fromEntries(keys.map(k => [
    k,
    id({ test: () => { } }) // ok
]))

奇怪的行为,不是吗?

让我们来看看 Object.fromEntries 签名:

fromEntries<T = any>(entries: Iterable<readonly [PropertyKey, T]>): { [k: string]: T };

因此,TS 尝试推断 T 泛型类型。在我们的例子中,它是 ReturnTypeid function

但是由于某些原因,TS 做不到。

第三:

为了帮助 TS 推断类型,您可以为 Array.prototype.map 提供显式泛型参数

const obj2 = Object.fromEntries(
  keys.map<[string, { test(): void }]>((k) => [k, id({ test() {} })])
);

第四

您也可以在没有任何显式泛型参数的情况下处理它。

您可以在 T 函数定义中向 id 泛型添加约束。

interface Method {
  (): void;
}
const id = <T extends { [prop: string]: Method }>(x: T) => x;

const keys: string[] = [];

const obj2 = Object.fromEntries(keys.map((k) => [k, id({ test() {} })])); // ok


/////////////////////////

type ArrowProp = () => any;
const id = <T extends { [prop: string]: ArrowProp }>(x: T) => x;

const keys: string[] = [];

const obj2 = Object.fromEntries(keys.map((k) => [k, id({ test() {} })]));

第五

你甚至可以使用第二个泛型来帮助 TS 推断类型

const id = <Prop, T extends Record<string, Prop>>(x: T) => x;
// OR
const id = <T extends object>(x: T) => x;


const keys: string[] = [];

const obj2 = Object.fromEntries(keys.map((k) => [k, id({ test() {} })]));

不幸的是,我无法解释为什么 TS 无法在没有任何解决方法的情况下推断方法属性

答案 1 :(得分:0)

立即尝试。

function id<T>(x: T): T {
   return x
}

const keys: string[] = []

const obj1 = Object.entries(keys.map(k => [
    k,
    { test() { } } // OK
]))

const obj2 = Object.entries(keys.map(k => [
    k,
    id({ test() { } }) // Ok
]))

const obj3 = Object.entries(keys.map(k => [
    k,
    id({ test: new Date() }) // OK
]))