打字稿:扩展字符串文字联合类型所需的值

时间:2021-04-06 23:46:18

标签: typescript

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

self.__class__.mro()

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

在代码中,我想要的是:

types.ts

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

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

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

1 个答案:

答案 0 :(得分:2)

一般来说,声明但未使用的类型参数是indicative of a problem。此外,您可能不想隐藏一个名为 Function 的全局对象。为了更易于分析,我会将您的示例修改为:

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

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

此处的 func()F 类型的值,即 constrainedFoo,并返回相同的值。因此类型签名是 <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 语法 (as in Java) 来支持这一点,因此人们可能会像目前所说的那样说 F super "a" 1}},但不直接支持这样的功能。

幸运的是,您可以使用 conditional types 来模拟下限。例如:

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 where F"a" extends F,不如说您想要一个类型参数 F extends Foo where {{1} },然后将 G 定义为 G extends Foo

特别是:

F

现在 G | "a" 不再可能不扩展 function func<G extends Foo>(f: G | "a") { return f; } // function func<G extends Foo>(f: G | "a"): G | "a" ,因为 "a" 只是我们给 F 和 {{1} 的并集起的名字}.当然,由于您正在更改类型参数的定义,它会相应地更改推理和 IntelliSense 显示:

F

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

Playground link to code