在构建某些库的类型安全版本时,我遇到了涉及复杂映射类型的问题并陷入困境。
这是此问题的简化版本。我们有两种“装箱”类型-Strong<T>
和Weak<T>
,可以分别认为它们是“持有价值”和“可能持有价值”。然后,有一个包含这些“框”的对象,我需要编写一个函数,通过将Strong
转换为类型为{的必填字段,将其统一到一个容纳对象的大Strong<T>
框中{1}}和T
到类型Weak<T>
的可选字段。稍后,在代码的另一部分中,我将“ T
”到“ Strong<T>
”的类型“拆箱”。
问题是我无法使这些字段成为条件可选的。有问题的代码如下:
T
一种尝试是获取所有对象值的并集,对其进行转换,然后使用UnionToIntersection
进行转换,但是类型足够复杂,以至于类型推断陷入困境并吐出一堆type Strong<T> = { __phantom?: T, marker: 'strong' };
type Weak<T> = { __phantom?: T, marker: 'weak' };
// helper functions, to be used later
function strong<T>(): Strong<T> {
return { marker: 'strong' };
}
function weak<T>(): Weak<T> {
return { marker: 'weak' };
}
// input type for my function
type Rec = { [index: string]: Strong<unknown> | Weak<unknown> };
// output type for my function; how to define it?
type Unified<T extends Rec> = { __phantom?: T, marker: 'unified' };
// put there something, just so that this type is unique
function unify<T extends Rec>(input: T): Strong<Unified<T>> {
// implementation is irrelevant now
return {} as any;
}
// when we have the Strong value, we can 'unbox' the type
type Unboxed<T> = T extends Strong<infer I> ? I : never;
// ...so, how to make this compile?
const unified = unify({ strong: strong<string>(), weak: weak<string>() });
const valid: Array<Unboxed<typeof unified>> = [{ strong: '' }, { strong: '', weak: '' }];
// ...and at the same time forbid this?
const invalid: Array<Unboxed<typeof unified>> = [{}, {weak: ''}, {unknown: ''}]
如何定义unknown
类型以正确地从输入中派生?
答案 0 :(得分:0)
在讨论反向问题的Github issue中找到了关键点:如何确定类型的字段是必填字段还是可选字段。这是那里提供的解决方案:
Unified
使用类似的技术,我能够解决此问题。首先,我们需要两种类似于上面的帮助器类型:
export type OptionalPropertyNames<T> = {
[K in keyof T]-?: undefined extends T[K] ? K : never
}[keyof T];
export type RequiredPropertyNames<T> = {
[K in keyof T]-?: undefined extends T[K] ? never : K
}[keyof T];
这里的关键点在于,当我们为该类型建立索引时,在对象类型上定义的type OptionalPropertyNames<T> = {
[K in keyof T]: T[K] extends Weak<any> ? K : never
}[keyof T];
type RequiredPropertyNames<T> = {
[K in keyof T]: T[K] extends Weak<any> ? never : K
}[keyof T];
值会有效消失。
然后,never
类型只是两个映射类型的交集,每个映射类型都将其自身的一部分值拆箱:
Unified