首页 > 解决方案 > 添加泛型方法会引发类型错误:对象字面量只能指定已知属性,而 __ 类型中不存在 __

问题描述

我的目标是 foo 接受任何扩展Obs<T>. 但是我收到以下错误。

Argument of type '{ reset: () => number; x: number; set: (x: number) => number; }' is not assignable to parameter of type 'Obs<unknown>'.
  Object literal may only specify known properties, and 'reset' does not exist in type 'Obs<unknown>'

片段 A

{
  // add a generic method 
  type Obs<T> = { x: T; set: (x: T) => T }
  function createObs<T>(x: T): Obs<T> {
    return { x, set: (val: T) => val }
  }
  function foo<O extends Obs<T>, T>(a: O) {}
  const a = createObs(1)
  foo({ ...a, reset: () => 1 }) // ❌ now throws the error
}

有趣的是,如果我删除该set方法,则类型会按预期工作。

片段 B

{
  type Obs<T> = { x: T }
  function createObs<T>(x: T): Obs<T> {
    return { x }
  }
  function foo<O extends Obs<T>, T>(a: O) {}
  const a = createObs(1)
  foo({ ...a, reset: () => 1 }) // ✅
}

TS游乐场链接

如何使 Snippet A 与泛型set方法一起使用?

标签: typescripttypescript-typings

解决方案


您需要添加额外的通用参数:

// U is extra generic parameter
type Obs<T> = { x: T; set: <U extends T>(x: U) => T }

function createObs<T>(x: T): Obs<T> {
  return { x, set: (val: T) => val }
}
function foo<O extends Obs<T>, T>(a: O) { }
const a = createObs(1)
a.set(2) // ok
foo({ ...a, reset: () => 1 })

操场

T在 中是协变的type Obs<T> = { x: T }。考虑这个例子:

type Obs<T> = { x: T }

let foo: Obs<number> = { x: 0 }

let bar: Obs<42> = { x: 42 }

foo = bar

bat可分配给foo

但是如果你添加一个set方法,就像这里:

type Obs<T> = { x: T, set: (x: T) => T }

T变得不变。


type Obs<T> = { x: T, set: (x: T) => T }

let foo: Obs<number> = { x: 0, set: (val) => val }

let bar: Obs<42> = { x: 42, set: (val) => val }

foo = bar // error
bar = foo // error

bar不再可分配给foo

因此,如果你想让它工作,你需要做set一个子类型的参数T

老实说,我不确定T becomes invariant。所以,如果你对此有任何争论,我愿意接受批评。

PS 更多关于 *-variance 你会发现here


推荐阅读