我编写了一个相当简单的基于映射类型的代码,由于某种原因它不想进行类型检查。
首先,定义输入和输出:
interface Validated<T> {
valid: boolean;
value: T;
}
interface FieldInputs {
name: string;
price: number;
}
interface ParsedFields {
name: Validated<string>;
price: Validated<number>;
}
定义解析器类型和解析器映射:
type FieldKey = keyof FieldInputs & keyof ParsedFields;
type FieldParser<F extends FieldKey> = (value?: FieldInputs[F]) => ParsedFields[F];
type FieldParsers = {
[F in FieldKey]: FieldParser<F>;
};
declare let fieldParsers: FieldParsers;
现在这个非常简单的通用函数无法进行类型检查:
function update<F extends FieldKey>(field: F, value: FieldInputs[F]) {
const parser: FieldParser<F> = fieldParsers[field];
parser.apply(value);
}
给出以下错误(--strictFunctionTypes
):
Type 'FieldParsers[F]' is not assignable to type 'FieldParser<F>'.
Type 'FieldParser<"name"> | FieldParser<"price">' is not assignable to type 'FieldParser<F>'.
Type 'FieldParser<"name">' is not assignable to type 'FieldParser<F>'.
Types of parameters 'value' and 'value' are incompatible.
Type 'FieldInputs[F]' is not assignable to type 'string'.
Type 'string | number' is not assignable to type 'string'.
Type 'number' is not assignable to type 'string'.
我错过了什么?
最佳答案
编译器正在保护您免受不太可能发生的事情的影响,您需要决定如何解决它(剧透警报:使用 type assertion )
想象一下如果我这样做:
const field = Math.random() < 0.5 ? "name" : "price";
const value = Math.random() < 0.5 ? "Widget" : 9.95;
update(field, value); // no error
在本例中,field
类型为FieldKey
和value
类型为FieldInputs[FieldKey]
,并且有 50% 的可能性它们不匹配。尽管如此,编译器不会警告您:它推断 F
是 FieldKey
(这对于它来说是完全有效的),以及对 update()
的调用是允许的。
里面执行update()
,有警告 FieldParsers[F]
可能不是FieldParser<F>
。如果F
是 FieldKey
如上所述,这种不匹配变得明显。 FieldParsers[F]
将是FieldParser<'name'> | FieldParser<'price'>
,但是FieldParser<F>
是 FieldParser<'name' | 'price'>
。前者是解析 string
的东西。 或解析 number
的东西。后者可以解析 string
。或number
。它们不相同(由于 contravariance of function parameters 使用 --strictFunctionTypes
启用)。当上面的代码最终调用 update("name", 9.95)
时,这些类型之间的差异就暴露出来了。然后你尝试解析 number
与 string
解析器。你想要一个FieldParser<F>
,但你拥有的只是 FieldParsers[F]
.
现在备份一下,是否有人可能玩这样的游戏,其中F
是值(value)观的结合?如果是这样,那么您可能想要更改 update()
的定义明确禁止F
不再是单个字符串文字。类似...
type NotAUnion<T, U = T> =
U extends any ? [T] extends [U] ? T : never : never;
declare function update<F extends FieldKey>(
field: F & NotAUnion<F>,
value: FieldInputs[F]
);
但这可能有点过分了,它仍然无法解决 update()
实现中的警告。 。编译器根本不够聪明,无法理解 F
的值。是单个字符串文字值,并且您所做的事情是安全的。
要消除该错误,您可能需要执行 type assertion 。要么你知道没有人会故意通过加宽来搬起石头砸自己的脚F
至FieldKey
,或者您已通过使用类似 NotAUnion
的内容阻止调用者执行此操作。无论哪种情况,您都可以告诉编译器您知道 fieldParsers[field]
将是有效的FieldParser<F>
:
function update<F extends FieldKey>(field, value: FieldInputs[F]) {
const parser = fieldParsers[field] as FieldParser<F>; // okay
parser.apply(value);
}
这样就可以了。希望有帮助。祝你好运!
关于typescript - 映射类型和泛型函数输入错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50610521/