在下面的示例中,我有一个函数,它将泛型类型V
作为参数,还有另一个函数也将V
作为参数。
当我用一个字符串和一个匿名函数调用该函数时,该函数从原始函数的签名中推断出其参数的类型,我会得到一个按预期键入的结果:string
。
然而,当我用符合签名的单独声明的函数调用它时,字符串参数的类型会缩小为字符串文字。
type Placeholder<V> = (value: V) => void;
const myTestFunction = <V extends any>(
value: V,
placeholder: Placeholder<V>
) => {
return value;
}
const aTypedFunction = (value: string) => { return };
// "isLiteralType"
const typedResult = myTestFunction("isLiteralType", aTypedFunction);
// "string"
const inferredResult = myTestFunction("isStringType", (value) => { return });
TS游乐场
这似乎是由于在aTypedFunction
中显式键入了value
参数,因为在匿名函数中显式输入参数也会产生字符串文字结果。然而,我不知道为什么会出现这种情况!
为什么会发生这种类型缩小,我如何更改myTestFunction
或aTypedFunction
,使前者接受后者作为参数而不缩小类型?
类型参数通常是从一个参数中推断出来的;对于函数,V
将从参数value: V
中推断出来,因为它的类型是原始类型参数,所以在调用函数时,可以很容易地推断出类型V
——只需查看调用中第一个参数的类型,并将其用于V
。这就是这里发生的事情,这就是为什么V
在调用myTestFunction("isLiteralType", aTypedFunction)
中被推断为"isLiteralType"
。
当您直接传递类型化的箭头函数时,同样适用。也就是说,我真的不知道为什么使用未键入的箭头函数会得到不同的结果。我和你一样对此感到好奇。
幸运的是,我们不需要弄清楚为什么第二个例子表现不同:有了类型参数通常会从更容易推断的参数中推断出来的知识,如果我们想从placeholder
而不是value
推断类型参数,就把它设为placeholder: P
而不是value: V
。我在下面写了一个解决方案,它从P
派生value
的类型,而不是从V
派生placeholder
的类型。
type Placeholder<V> = (value: V) => void;
type PlaceholderComponent<P extends Placeholder<any>> = P extends Placeholder<infer V> ? V : never
const myTestFunction = <P extends Placeholder<any>>(
value: PlaceholderComponent<P>,
placeholder: P
) => {
return value;
}
const aTypedFunction = (value: string) => { return };
// string
const typedResult = myTestFunction("isLiteralType", aTypedFunction);
还要注意,使用非类型化回调的调用将导致any
而不是string
;这是有道理的,因为没有类型注释的参数在无法从上下文推断其类型时,默认情况下具有类型any
。
游乐场链接