繁体   English   中英

Typescript:扩展字符串文字联合类型中的必需值

[英]Typescript: Required value in extending string literal union type

(目前使用 Typescript 4.3)假设我定义了以下联合类型以及一些通用 function

types.ts

export type Foo = 'a' | 'b' | 'c'

export function Function<F extends Foo>() { ... }

我想做的是以某种方式使它,以便 Function 的任何通用使用都应始终包含类型“a”(即扩展 Foo 的任何 F 都必须包含“a”)

在代码中,我想要的是:

Function<'a' | 'b'>() // succeeds
Function<'c'>() // fails
Function<'a'>() // succeeds

有什么方法可以修改 Foo 或 Function 的类型定义来实现这一点?

一般而言,已声明但未使用的类型参数表明存在问题 此外,已经存在一个名为Function的全局 object,您可能不想对其进行跟踪。 为了更易于分析,我会将您的示例修改为:

type Foo = 'a' | 'b' | 'c';

function func<F extends Foo>(f: F) { return f; } 
// function func<F extends Foo>(f: F): F

这里func()采用F类型的值,该值被限制Foo ,并返回相同的值。 因此类型签名是<F extends Foo>(f: F) => F

当您调用func()时,您可以手动指定类型参数F

const x = func<"a" | "b">(Math.random() < 0.5 ? "a" : "b");
// const x: "a" | "b"

但这并不比让编译器推断它更好:

const y = func(Math.random() < 0.5 ? "a" : "b");
// const y: "a" | "b"

所以从这里开始,我将让编译器推断类型参数:


您要求确保为F指定的任何内容都包括"a" 换句话说,您希望F上限Foo下限"a"

所以以下应该成功:

func(Math.random() < 0.5 ? "a" : "b"); // okay
func("a") // okay

以下应该失败:

func(Math.random() < 0.5 ? "a" : "d"); // fails
func("c") // should fail but doesn't

现在func("c")失败


不幸的是,TypeScript 中没有语法直接支持以这种方式“从下方”约束类型参数。 microsoft/TypeScript#14520有一个长期存在的功能请求,以支持使用super语法( 如 Java ),因此人们可能会说F super "a"之类的东西,就像人们目前所说的F extends Foo一样,但是这样的功能不直接支持。

幸运的是,您可以使用条件类型来模拟下限。 例如:

function func<F extends ("a" extends F ? Foo : "a")>(f: F) { return f; }
// function func<F extends "a" extends F ? Foo : "a">(f: F): F

这可能很难理解,但它强制F extends Foo"a" extends F (后者是F super "a"的另一种说法)。 它的行为方式如你所愿:

func(Math.random() < 0.5 ? "a" : "b"); // okay
func("a") // okay
func("c") // fails

但是,在您采用这样的策略之前,可能值得考虑一下这是否是必要的。 如果您更改类型参数定义,您可能只需要一个上限就可以逃脱。

与其说你想要一个类型参数F ,其中"a" extends F并且F extends Foo ,不如说你想要一个类型参数G ,其中G extends Foo ,然后将F定义为G | "a" G | "a"

尤其是:

function func<G extends Foo>(f: G | "a") { return f; }
// function func<G extends Foo>(f: G | "a"): G | "a"

现在"a"不再可能不扩展F ,因为F只是我们给G"a"并集的一个名称。 当然,由于您正在更改类型参数的定义,它会相应地更改推理和 IntelliSense 显示:

func(Math.random() < 0.5 ? "a" : "b"); // okay
// function func3<"b">(f: "a" | "b"): "a" | "b"

func("a") // okay
// function func<"a">(f: "a"): "a"

func(Math.random() < 0.5 ? "a" : "d"); // fails

func("c") // okay!
// function func<"c"> (f: "a" | "c"): "a" | "c"

最后一次调用不会失败,因为值"c"的类型是"a" | "c" "a" | "c" 这种类型的调用失败是否重要可能会影响您是否可以 go 这条路线。 但是对于某些用例,这可能比显式下限仿真更可取。

Playground 代码链接

暂无
暂无

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

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