[英]How to specialize type of discriminated union?
Assume I have two discriminated unions and a third type alias which is a union type containing both.假设我有两个可区分的联合和第三种类型的别名,它是包含两者的联合类型。
How do I create a function which can take both the discriminated unions but whose return type depends on the specific type of the value passed?如何创建一个 function 可以同时采用可区分的联合,但其返回类型取决于传递的值的特定类型?
I thought this would work, given that Specialize
would return { value: number }
if the passed value belongs to First
and number
if it belongs to Second
, but it doesn't.我认为这会起作用,因为如果传递的值属于
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;
}
What am I misunderstanding here?我在这里有什么误解?
Please notice that I do not want to remove the R
which constrains the return type.请注意,我不想删除限制返回类型的
R
。 Instead, I'd like to constrain the return type to the passed argument.相反,我想将返回类型限制为传递的参数。
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; }'.
This is a job for overloads.这是一项超载的工作。 By providing specific overloads for narrowed cases, the return value will also be narrowed.
通过为缩小的情况提供特定的重载,返回值也将缩小。
// 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
Your generics-based approach can't work because the caller gets to decide what R
is, and R
can be any type that extends Specialize<M>
.您的基于泛型的方法无法工作,因为调用者可以决定
R
是什么,并且R
可以是扩展Specialize<M>
的任何类型。 That's what the errors are telling you.这就是错误告诉你的。 You have no way to return the specific subtype the caller has chosen.
您无法返回调用者选择的特定子类型。
(Given TypeScript's structural typing, I'm not aware of particular way to create a subtype of Specialize<R>
. But the compiler can't prove that it's impossible, and I'm sure someone more clever with TypeScript than I am can think of one. But by using extends
you specifically said that the caller can require a subtype, and that makes returning R
impossible.) (鉴于 TypeScript 的结构类型,我不知道创建
Specialize<R>
子类型的特定方法。但编译器无法证明这是不可能的,而且我敢肯定 TypeScript 比我想象的更聪明一个。但是通过使用extends
,您明确表示调用者可能需要一个子类型,这使得返回R
是不可能的。)
The main problem is the polymorphism given by the generic constraint.主要问题是泛型约束给出的多态性。
Defining your function as generic with an argument type which extends from another, you must ensure your return
statement actually returns a value which is assignable of type T
and its extensions (where T
is the type argument).将 function 定义为具有从另一个扩展的参数类型的泛型,您必须确保您的
return
语句实际上返回一个可分配类型T
及其扩展的值(其中T
是类型参数)。
Let's suppose you define this type假设您定义了这种类型
type MoreSpecialize<R> = R extends First ? { value: 10 } : 20;
... which extends from Specialize<>
. ...从
Specialize<>
扩展而来。 I could invoke the function fun
with this type argument,我可以用这个类型参数调用 function
fun
,
const r: MoreSpecialize<Letter> = fun2<Letter, MoreSpecialize<Letter>>('a');
But, r
is not actually of type MoreSpecialize<Letter>
because fun
returns { value: 1 } | 2
但是,
r
实际上不是MoreSpecialize<Letter>
类型,因为fun
返回{ value: 1 } | 2
{ value: 1 } | 2
, not { value: 10 } | 20
{ value: 1 } | 2
,不是{ value: 10 } | 20
{ value: 10 } | 20
. { value: 10 } | 20
. That's why the compiler complains.这就是编译器抱怨的原因。
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;
}
Hope it helps.希望能帮助到你。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.