typescript - 为什么 TS 不会将我的类型参数完全求值,我该如何解决这个问题?
我有一个泛型类型,它有点复杂,包括一个联合类型,其中只有在类型参数 T 扩展了 undefined 时才允许某个元素。
type PropValidator<T> = {
[k in keyof T & string]:
// simple form - just a function, implies required
| Function
// validate a required property
| {
required: true;
validate: Function;
}
// validate a property that is allowed to be undefined
| (undefined extends T[k] // <-- this is the key part
? {
required: false;
validate: Function;
}
: never);
};
我理解这意味着 { required: false, validate:() => {} } 不是有效的,除非 T 上的属性允许 undefined 作为值。所以在你不知道 undefined extends T[K] 是否成立的泛型上下文中无法使用它是有道理的。
但是在 T 已经被约束的上下文中呢?例如,SomeType:
// something you might validate
type SomeType<T = string> = {
prop?: T
}
// a type that only validates SomeType objects
type SomeTypeValidator<T> = PropValidator<SomeType<T>>
我们知道 undefined 总是会 extend SomeType<T>['prop'],因为 prop 在 SomeType 定义中是可选的,因此 SomeTypeValidator<T> 应该总是允许 { required: false, validate:() => {} }
但是当我尝试在通用上下文中给它设置一个值时,它会拒绝这一操作:
function constrainedGeneric<T extends string>(prop: SomeTypeValidator<T>['prop']) {
prop = {
required: false, // Type '{ required: false; validate: () => void; }' is not assignable to type 'Function | { required: true; validate: Function; } | (undefined extends T | undefined ? { required: false; validate: Function; } : never)'.
// Object literal may only specify known properties, and 'required' does not exist in type 'never'.'SomeTypeValidator<T>'
validate: () => {}
}
}
我哪里出了错,我该如何解决这个问题?我如何修改我的定义,使其意思不变,但允许我在 constrainedGeneric 函数中设置那个字面量 { required: false } 值?
便捷的 Playground 链接
答案#1
(我不得不稍微重构一下,以便一开始对我来说更清楚。)
我几乎可以通过在最后的赋值中对对象进行类型转换(见代码)使其完全工作,并且无需特定的类型参数约束:现在编译按预期成功/失败,并且该 propValidator 的单个成员看起来也没问题。
唯一的问题是,这可能是 TS(v5.2.2)中条件类型的一个限制,即在最后一种情况下,propValidator 的类型没有被“解析”:不过,无论如何,返回它似乎工作正常(见 test 函数的返回类型)...
type SimplePropValidator = Function;
type RequiredPropValidator = {
required: true;
validate: Function;
};
type OptionalPropValidator = {
required: false;
validate: Function;
};
type ObjectValidator<T> = {
[K in keyof T]:
| SimplePropValidator
| RequiredPropValidator
| (undefined extends T[K] ? OptionalPropValidator : never);
};
type SomeType<P> = { prop?: P; }
type SomeTypeValidator<P> = ObjectValidator<SomeType<P>>
function test<P>(): OptionalPropValidator {
let propValidator: SomeTypeValidator<P>["prop"];
propValidator = () => { };
propValidator; // Function
propValidator = {
required: true,
validate: () => { }
};
propValidator; // RequiredPropValidator
// fails compilation if `prop` is NOT optional, as expected:
propValidator = {
required: false,
validate: () => { }
} as OptionalPropValidator;
propValidator; // undefined extends P | undefined ? OptionalPropValidator : never
propValidator.required; // false
propValidator.validate; // Function
return propValidator;
}
游乐场链接
附录:
假设这与条件类型有关,我尝试了这个变体,确实它有效:在最后一次赋值之后,propValidator 的类型会被解析(不是立即解析,而是在下一行解析,参见 playground 中的代码),并且不需要任何类型转换...
附言再附言 但现在这种行为发生在每一个作业上,而不仅仅是最后一次:类型只有在第一次使用后才会被解析(不过我没有费力去修正游乐场里的注释)。所以我必须收回之前认为这比之前的解决方案更令人满意的说法。
正如我在评论中提到的,我认为这是 TypeScript 处理条件类型时的一个(很可能的)限制/错误,可能值得在 GitHub 上报告为一个问题。
type ObjectValidator<T> = { // refactored definition
[K in keyof T]: (
undefined extends T[K] ?
(SimplePropValidator | RequiredPropValidator | OptionalPropValidator) :
(SimplePropValidator | RequiredPropValidator)
);
};
游乐场链接(2)
