繁体   English   中英

如何专门化歧视工会的类型?

[英]How to specialize type of discriminated union?

假设我有两个可区分的联合和第三种类型的别名,它是包含两者的联合类型。

如何创建一个 function 可以同时采用可区分的联合,但其返回类型取决于传递的值的特定类型?

我认为这会起作用,因为如果传递的值属于Firstnumber如果属于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 . 这就是编译器抱怨的原因。

解决方案

  1. 如果您确实不需要扩展,请使用具体类型定义您的 function。
const fun = (letter: Letter): Specialize<Letter> => {
    return letter === "a" || letter === "b"
        ? { value: 1 }
        : 2;
}
  1. 强制转换返回值 [.]。
    不是 go 的最佳方式。
const fun2 = <M extends Letter, R extends Specialize<M>>(letter: M): R => {
    return (
        letter === "a" || letter === "b"
            ? { value: 1 }
            : 2
    ) as R;
}
  1. @RobNapier 的解决方案完全没问题。

希望能帮助到你。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM