Typescript 如何专门化受歧视的工会类型?
假设我有两个有区别的联合和第三个类型别名,它是包含这两个联合的联合类型 如何创建一个函数,该函数可以接受两个有区别的联合,但其返回类型取决于传递的值的特定类型 如果传递的值属于Typescript 如何专门化受歧视的工会类型?,typescript,Typescript,假设我有两个有区别的联合和第三个类型别名,它是包含这两个联合的联合类型 如何创建一个函数,该函数可以接受两个有区别的联合,但其返回类型取决于传递的值的特定类型 如果传递的值属于First和number的话,Specialize会返回{value:number},如果它属于Second的话,我认为这是可行的,但是它不会返回 type First=“a”|“b”; 键入Second=“c”|“d”; 输入字母=第一个|第二个; 类型Specialize=R首先扩展?{value:number}:nu
First
和number
的话,Specialize
会返回{value:number}
,如果它属于Second
的话,我认为这是可行的,但是它不会返回
type First=“a”|“b”;
键入Second=“c”|“d”;
输入字母=第一个|第二个;
类型Specialize=R首先扩展?{value:number}:number;
const fun=(字母:M):R=>{
返回字母==“a”|字母==“b”
?{value:1}
: 2;
}
我在这里误解了什么
请注意,我不想删除约束返回类型的R
。相反,我希望将返回类型约束为传递的参数
这是一个重载作业。通过为缩小的情况提供特定的重载,返回值也将缩小
// 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
可以是扩展专门化的任何类型。这就是错误告诉你的。您无法返回调用方选择的特定子类型
(考虑到TypeScript的结构类型,我不知道创建Specialize
子类型的具体方法。但是编译器无法证明这是不可能的,而且我相信比我更聪明的人可以想到一个。但是通过使用extends
,你明确地说调用方可能需要子类型,并且t使得返回R
不可能。)主要问题是泛型约束给出的多态性
将函数定义为泛型函数,并使用从另一个类型扩展而来的参数类型,必须确保return
语句实际返回一个值,该值可赋值为T
类型及其扩展名(其中T
是类型参数)
假设您定义了这个类型
type MoreSpecialize<R> = R extends First ? { value: 10 } : 20;
但是,r
实际上不是moreespecialize
类型,因为fun
返回{value:1}2
,而不是{value:10}20
。这就是编译器抱怨的原因
解决方案
如果确实不需要扩展,请使用具体类型定义函数
const-fun=(字母:字母):Specialize=>{
返回字母==“a”|字母==“b”
?{value:1}
: 2;
}
强制强制转换返回值[!]。
这不是最好的方式
constfun2=(字母:M):R=>{
返回(
字母==“a”|字母==“b”
?{value:1}
: 2
)as-R;
}
@RobNapier的解决方案非常好
希望能有帮助
type MoreSpecialize<R> = R extends First ? { value: 10 } : 20;
const r: MoreSpecialize<Letter> = fun2<Letter, MoreSpecialize<Letter>>('a');
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;
}