[英]How to specialize type of discriminated union?
假设我有两个可区分的联合和第三种类型的别名,它是包含两者的联合类型。
如何创建一个 function 可以同时采用可区分的联合,但其返回类型取决于传递的值的特定类型?
我认为这会起作用,因为如果传递的值属于First
和number
如果属于Second
,则Specialize
将返回{ value: number }
,但事实并非如此。
type First = "a" | "b";
type Second = "c" | "d";
type Letter = First | Second;
type Specialize<R> = R extends First ? { value: number } : number;
const fun = <M extends Letter, R extends Specialize<M>>(letter: M): R => {
return letter === "a" || letter === "b"
? { value: 1 }
: 2;
}
我在这里有什么误解?
请注意,我不想删除限制返回类型的R
。 相反,我想将返回类型限制为传递的参数。
Type '{ value: number; } | 2' is not assignable to type 'R'.
'{ value: number; } | 2' is assignable to the constraint of type 'R', but 'R' could be instantiated with a different subtype of constraint 'number | { value: number; }'.
Type '{ value: number; }' is not assignable to type 'R'.
'{ value: number; }' is assignable to the constraint of type 'R', but 'R' could be instantiated with a different subtype of constraint 'number | { value: number; }'.
这是一项超载的工作。 通过为缩小的情况提供特定的重载,返回值也将缩小。
// Overloads
function fun(letter: First): { value: number };
function fun(letter: Second): number;
// General case
function fun(letter: Letter): { value: number } | number {
return letter === "a" || letter === "b"
? { value: 1 }
: 2;
}
const x = fun("a") // x: { value: number }
const y = fun("c") // y: number
您的基于泛型的方法无法工作,因为调用者可以决定R
是什么,并且R
可以是扩展Specialize<M>
的任何类型。 这就是错误告诉你的。 您无法返回调用者选择的特定子类型。
(鉴于 TypeScript 的结构类型,我不知道创建Specialize<R>
子类型的特定方法。但编译器无法证明这是不可能的,而且我敢肯定 TypeScript 比我想象的更聪明一个。但是通过使用extends
,您明确表示调用者可能需要一个子类型,这使得返回R
是不可能的。)
主要问题是泛型约束给出的多态性。
将 function 定义为具有从另一个扩展的参数类型的泛型,您必须确保您的return
语句实际上返回一个可分配类型T
及其扩展的值(其中T
是类型参数)。
假设您定义了这种类型
type MoreSpecialize<R> = R extends First ? { value: 10 } : 20;
...从Specialize<>
扩展而来。 我可以用这个类型参数调用 function fun
,
const r: MoreSpecialize<Letter> = fun2<Letter, MoreSpecialize<Letter>>('a');
但是, r
实际上不是MoreSpecialize<Letter>
类型,因为fun
返回{ value: 1 } | 2
{ value: 1 } | 2
,不是{ value: 10 } | 20
{ value: 10 } | 20
. 这就是编译器抱怨的原因。
const fun = (letter: Letter): Specialize<Letter> => {
return letter === "a" || letter === "b"
? { value: 1 }
: 2;
}
const fun2 = <M extends Letter, R extends Specialize<M>>(letter: M): R => {
return (
letter === "a" || letter === "b"
? { value: 1 }
: 2
) as R;
}
希望能帮助到你。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.