如何解决在这种特定情况下,为什么使用映射类型解析为联合?
我最近偶然发现了以下TypeScript代码:
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never
将string | number
之类的联合转换为交集string & number
。
经过大量研究,我认为我很了解底层机制,并开始对其进行修补以打破它。
我的测试之一是构建一个具有特定重载的函数类型,具体取决于以下映射:
type Mapping = {
one: {
arg: true;
ret: string;
};
two: {
arg: string;
ret: number;
};
};
// Goal is to make this:
type Goal = ((arg: true) => string) & ((arg: string) => number);
为此,我使用了映射类型来构建函数,并依靠UnionToIntersection
将它们组合为重载:
// Resolves to union of all values in record
type ValueOf<T extends Record<string,unknown>> = T[keyof T];
type BuildOverloadedFunction<
T extends Record<
string,{
arg: unknown;
ret: unknown;
}
>
> = UnionToIntersection<ValueOf<
{
[P in keyof T]: (arg: T[P]["arg"]) => T[P]["ret"];
}
>>;
// Result
type Result = ((arg: true) => string) & ((arg: string) => number)
(出于完整性考虑,提供了{ValueOf
)
到目前为止一切顺利。
我以为事情开始变得奇怪
让我们删除
extends
中的第一个UnionToIntersection
,以便解析的类型将成为参数类型(true & string
->never
)的交集
其中提供了以下代码(为清楚起见,将其重命名为NakedInfer
):
type NakedInfer<T> = T extends (k: infer I) => void ? I : never;
令我惊讶的是它解析为true | string
。
然后我注意到我使用了裸露的T
,该裸露的type ClothedInfer<T> = [T] extends [(k: infer I) => void] ? I : never;
将被分发并执行以下更改:
T
现在BuildOverloadedFunction
穿着适当的衣服,但结合仍然存在。
经过更多修改后,我更改了never
类型以删除映射的类型,然后突然按预期解析为type Test2<
T extends Record<
string,{
arg: unknown;
ret: unknown;
}
>
> = ClothedInfer<ValueOf<
{
one: (arg: true) => string;
two: (arg: string) => number;
}
>>;
:
// Resolves to union of all values in record
type ValueOf<T extends Record<string,unknown>> = T[keyof T];
// Distributed thanks to distributive conditional types --> no intersection
type NakedInfer<T> = T extends (k: infer I) => void ? I : never;
// Not distributed because clothed --> Union in contra-variant position --> intersection
type ClothedInfer<T> = [T] extends [(k: infer I) => void] ? I : never;
// Expanded infer. Resolves to never (true & string)
type Step1 = ValueOf<{
one: (arg: true) => string;
two: (arg: string) => number;
}> extends (k: infer I) => void ? I : never;
// Distributed. Resolves to union (true | string)
type Step2 = NakedInfer<ValueOf<{
one: (arg: true) => string;
two: (arg: string) => number;
}>>;
// Not distributed. Resolves to never (true & string)
type Step3 = ClothedInfer<ValueOf<{
one: (arg: true) => string;
two: (arg: string) => number;
}>>;
// More complex example
type Mapping = {
one: {
arg: true;
ret: string;
};
two: {
arg: string;
ret: number;
};
};
// Builds functions based on the provided Mapping using mapped types.
// Expands to exactly the same thing used above and below.
// Unexpectedly resolves to union
type Test1<
T extends Record<
string,{
arg: unknown;
ret: unknown;
}
>
> = ClothedInfer<ValueOf<
{
[P in keyof T]: (arg: T[P]["arg"]) => T[P]["ret"];
}
>>;
// Same as Test1 but the mapped types and function building is omitted.
// Properly resolves to never
type Test2<
T extends Record<
string,{
arg: unknown;
ret: unknown;
}
>
> = ClothedInfer<ValueOf<
{
one: (arg: true) => string;
two: (arg: string) => number;
}
>>;
// Both t1 and t2 should resolve to never
declare const t1: Test1<Mapping>; // string | true
declare const t2: Test2<Mapping>; // never
我现在想我可能会错过一些与映射类型有关的特定行为,并且想知道是否是这种情况或是否犯了错误。
下面是带有一些注释的完整代码(还有here is the playground link):
var key = {
"1" : "ID 1: Steve","2" : "ID 2: Bob","3" : "ID 3: Paul","4" : "ID 4: Spencer","5" : "ID 5: Jimmy"};
// const desired = ["ID 1: Steve","ID 3: Paul","ID 5: Jimmy"]
const data = ["1","3","5"]
const result = []
const values = Object.values(key)
//console.log(values);
data.forEach(item => {
values.forEach(val => {
if (val.includes('ID ' + item + ':')) {
result.push(val)
}
})
})
console.log(result);
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。