此代码从CSV编写库简化而来:
(我现在使用的是UnionToIntersection
助手:https://stackoverflow.com/a/50375286/163832)
type UnionToIntersection<U> =
(U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
type Spec = 'string' | 'timestamp' | ['nullable', Spec];
type SpecToValue<T> = UnionToIntersection<
T extends 'string' ? string :
T extends 'timestamp' ? Date :
T extends ['nullable', infer InnerT] ? SpecToValue<InnerT> | null :
never
>;
declare function writeValue<T extends Spec>(spec: T, value: SpecToValue<T>): void;
function test(s1: 'string', s2: 'string' | 'timestamp', s3: ['nullable', 'string']) {
writeValue(s1, 'hello');
writeValue(s1, false); // error (good)
writeValue(s1, null); // error (good)
writeValue(s2, 'hello'); // error (good)
writeValue(s2, false); // error (good)
writeValue(s2, null); // error (good)
// Problem: SpecToValue<['nullable', 'string']> resolves to `never` instead of `string | null`.
writeValue(s3, 'hello'); // ERROR (bad; I want this to be allowed)
writeValue(s3, false); // error (good)
writeValue(s3, null); // ERROR (bad; I want this to be allowed)
我认为问题是,UnionToIntersection
号高速公路也会将SpecToValue<InnerT> | null
号高速公路转换为十字路口,但我不想这样做.我想把它作为一个unions 保留下来.
有办法做到这一点吗?
Edit:在我的实际代码中,我将"简单"类型与"可空"类型分开;不确定这是否会对解决方案产生很大影响:
type SimpleSpec = 'string' | 'timestamp';
type Spec = SimpleSpec | ['nullable', SimpleSpec];
type SimpleSpecToValue<T> =
T extends 'string' ? string :
T extends 'timestamp' ? Date :
never;
type SpecToValue<T> =
T extends SimpleSpec ? SimpleSpecToValue<T> :
T extends ['nullable', infer InnerT] ? (SpecToValue<InnerT> | null) : null
(不过,如果我必须把这些定义结合起来,这并不是交易的 destruct 者.)