网站搜索

typescript - 在有边界的泛型类型上使用 Partial<T> 的问题


function f<T extends {id: string}>(id: T['id']): Partial<T> {
  return {id: id}; // ERROR: Type '{ id: T["id"]; }' is not assignable to type 'Partial<T>'
}

(TypeScript Playground 链接)

我正在尝试弄清楚为什么上面的代码不被允许。它本质上是错误的吗?还是因为 TypeScript 类型系统的限制?

答案#1

这是一个缺失的功能;编译器无法将该赋值视为安全,并且出于与“为什么我不能返回一个泛型 'T' 来满足 Partial?”中相同的原因而拒绝它,尽管允许它实际上是安全的。在 microsoft/TypeScript#22229 中有一个请求解决该问题的议题,但它被关闭,理由是“按预期工作”。从讨论来看,这看起来只是一个缺失的功能或设计上的限制。

如果你愿意,你可以使用类型断言:

function fAssert<T extends {id: string}>(id: T['id']): Partial<T> {
  return {id: id} as Partial<T>; // assert
}

或者你可以分阶段构建返回值(尽管这种方法过于宽松,并允许一些可能不会很快被修复的不可靠的东西):

function fRoundabout<T extends {id: string}>(id: T['id']): Partial<T> {
  const ret: Partial<T> = {}; // allowed
  ret.id = id; // also works
  return ret;
}

我不完全确定你为什么想要将返回类型设为 Partial<T>,因为这种类型在 T 的键上可能有值也可能没有值,而你返回的对象肯定有一个 id 属性,且没有其他属性。在我看来,这更像是 Pick<T, "id"> 而不是 Partial<T>:

function fDifferentReturnType<T extends { id: string }>(id: T['id']): Pick<T, 'id'> {
  return { id: id }; // okay
}

由于这个例子只是一个玩具示例,你的实际用例很可能需要 Partial<T> 而不是 {id: T['id']}。但如果不需要,那么你可能会决定完全绕开 Partial 问题。

好的,希望其中一个建议有帮助。祝你好运!

游乐场链接

答案#2

jcalz 提出了 3 个选项。哪一个合适?

我同意他的观点,即从这个“玩具”示例中,并不清楚是否真的需要返回类型 Partial<T>。Pick<T, 'id'> 就足够了。在这种情况下,类型推断工作得很好,而且可能是一个更简单的解决方案:

function f<T extends {id: string}>(id: T['id']) {
  return { id }; // type: { id: T['id'] }
}