首页 > 解决方案 > 使用以 T 为条件的其他类型的类型断言来推断类型 T

问题描述

考虑这个检索“项目”或“项目”数组的函数:

function foo<T extends Item | Item[]>(id: T extends Item ? string : undefined): T {
  ...
}

这样做的目的是,如果您想要一个项目,则foo使用 an调用,或者如果您想要一个项目数组,则不使用 an 调用。这很有效,因此调用者可以这样做:idid

foo<MyItem>('42')

foo<MyItem[]>(undefined)

(使用可变参数可以使这一点变得更好,例如在检索数组时function foo<T extends Item | Item[]>(...[id]: T extends Item ? [string] : []): T {启用不带参数的调用)。foo

但是,在 的实现中foo,我注意到如果我检查 的类型id

if (id) { // if typeof id === 'string' gives same result
  ...
}

...然后在那个 if 子句中,编译器不明白该类型T现在必须扩展Item不是 Item[](因为id仅当 是一个字符串。如果我想调用任何依赖于 T 的代码,T extends Item我仍然需要这样做,或者我会得到:T as ItemItem

TS2344: Type 'T' does not satisfy the constraint 'Item'.   Type 'Item | Item[]' is not assignable to type 'Item'.     Property 'id' is missing in type 'Item[]' but required in type 'Item'.

换句话说, 的类型id取决于 的类型,但 T 的类型不会通过断言 that是一个字符串T来推断/缩小。id有可能以某种方式解决这个问题吗?这是一个简单的as演员,但我只是好奇。

标签: typescripttypescript-generics

解决方案


您可以使返回类型以参数类型为条件,而不是相反:

function foo<T extends string | undefined>(id: T): T extends string ? Item : Item[] {
  // ...
}

或者,如果你只想用绝对是字符串或绝对未定义的东西来调用它,你可以使用重载而不是让它泛型:这样你就可以在没有参数的情况下调用函数,而不是作为undefined传递争论。

function foo(): Item[];
function foo(id: string): Item;
function foo(id?: string): Item | Item[] {
  // ...
}

这些解决方案之间的实际区别在于,使用重载,您的函数不能使用 type 的参数调用string | undefined,因此在编译时始终知道特定调用将解析为单个项目还是项目数组。

请注意,这两种解决方案都避免了使用类型参数调用函数Item & HTMLCanvasElement然后返回类型不正确的问题。


推荐阅读