hea*_*low 7 generics typescript typescript-generics typescript-typings
我试图用简单的 Javascript 编写的内容是:
const cleanVar = (var1): T => var1?.replace(searchValue, replaceValue);
在 Typescript 中,我希望能够将其键入为:
const cleanVar = <T extends string | undefined>(var1: T): T => var1?.replace(searchValue, replaceValue);
但我收到这个错误:
Type 'string | undefined' is not assignable to type 'T'.
'string | undefined' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | undefined'.
Type 'undefined' is not assignable to type 'T'.
'undefined' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'string | undefined'.
Run Code Online (Sandbox Code Playgroud)
那么,输入这个函数的正确方法是什么?
含义应该是:var可以是string或者undefined。ifvar是string返回类型必须是string. 如果var是,则undefined返回类型必须是undefined。
jca*_*alz 11
cleanVar()return是不正确的T,因为存在string 文字类型,因此如果您调用cleanVar("hello"),则推断的类型T将是"hello"而不是string。即使您可以安排对 的调用replace(),使其在运行时实际上是一个恒等函数(例如, ),根据 TS 标准库,v.replace("a","a")编译器所知道的所有返回类型就是它是一个. 由于包含不可分配给 的值,编译器正确地抱怨。String.prototype.replace() stringstringT
“正确”的调用签名cleanVar()可能是这样的:
declare const cleanVar: <T extends string | undefined>(v: T) =>
T extends string ? string : undefined;
Run Code Online (Sandbox Code Playgroud)
也就是说,它应该返回一个条件类型,该类型分布在 中的联合上T。有了这个签名,让我们验证它在调用时是否按照您想要的方式工作:
const x = cleanVar("hello") // const x: string
const y = cleanVar(undefined) // const y: undefined;
const z = cleanVar(Math.random() < 0.5 ? "hello" : undefined) // const z: string | undefined
Run Code Online (Sandbox Code Playgroud)
看起来不错!
不幸的是,您的实现中仍然会出现编译器错误:
const cleanVar = <T extends string | undefined>(v: T):
T extends string ? string : undefined =>
v?.replace(searchValue, replaceValue); // error!
// Type 'string | undefined' is not assignable to type 'T extends string ? string : undefined'.
Run Code Online (Sandbox Code Playgroud)
这是因为编译器无法真正验证任何特定值是否可分配给条件类型,该条件类型取决于未解析的泛型类型参数,例如T在 的实现体内cleanVar。它推迟了对此类条件类型的评估,这些条件类型仍然是“不透明的”。这是 TypeScript 中的一个已知痛点,并且microsoft/TypeScript#33912上有一个未解决的问题,要求提供一些解决方案,从而T可以根据控制流分析缩小类型参数的范围。现在将从到内部v?.xyz缩小,但是您在函数中所做的任何操作都无法将自身从缩小到。我们被困住了。vTstringxyzTT extends string | undefinedT extends string
目前唯一的方法是使用类型断言;验证实现正确性的责任从编译器(无法做到这一点)转移到了您身上......所以要小心。无论如何,它看起来像这样:
const cleanVar = <T extends string | undefined>(v: T) =>
v?.replace(searchValue, replaceValue) as T extends string ? string : undefined;
Run Code Online (Sandbox Code Playgroud)
另一种“正确”的继续方法是创建cleanVar()一个具有多个调用签名的重载函数,如下所示:
function cleanVar(v: string): string;
function cleanVar(v: undefined): undefined;
function cleanVar(v: string | undefined): string | undefined;
function cleanVar(v: string | undefined) {
return v?.replace(searchValue, replaceValue);
}
Run Code Online (Sandbox Code Playgroud)
这与通用条件版本的工作方式类似,但更加冗长。如果没有类型断言,您无法轻松地将其设为箭头函数(违背了目的),即使使用上面的函数语句,实现中缺少错误类似于使用类型断言...编译器只关心实现签名匹配。如果你把它改成
function cleanVarBad(v: string): string;
function cleanVarBad(v: undefined): undefined;
function cleanVarBad(v: string | undefined): string | undefined;
function cleanVarBad(v: string | undefined): string | undefined {
return Math.random() < 0.5 ? "oopsie" : undefined;
}
Run Code Online (Sandbox Code Playgroud)
那么你仍然不会收到错误,但你可能会在运行时非常不高兴。因此,两者实际上并没有比另一者有什么大的好处。我通常更喜欢通用条件调用签名而不是重载。
| 归档时间: |
|
| 查看次数: |
5962 次 |
| 最近记录: |