typescript - 打字稿:扩展字符串文字联合类型中的必需值
问题描述
(目前使用 Typescript 4.3)假设我定义了以下联合类型以及一些通用函数
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
您可能不想遮蔽的全局对象。为了更易于分析,我会将您的示例修改为:
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
and "a" extends F
(后者是另一种说法F super "a"
)。它的行为方式如你所愿:
func(Math.random() < 0.5 ? "a" : "b"); // okay
func("a") // okay
func("c") // fails
但是,在您采用这样的策略之前,可能值得考虑一下这是否是必要的。如果您更改类型参数定义,您可能只需要一个上限就可以逃脱。
与其说你想要一个类型参数F
where"a" extends F
和F extends Foo
,你可以等效地说你想要一个类型参数G
where 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
,因为F
这只是我们给 and 的并集起的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"
。这种类型的呼叫失败是否重要可能会影响您是否可以走这条路。但是对于某些用例,这可能比显式下限仿真更可取。
推荐阅读
- html - 图像上的css过滤器亮度而不是文本覆盖
- ios - Xcode 11.1 - 找不到身份命令 PhaseScriptExecution 失败,退出代码为非零
- c# - UWP UserControl 大小正确但未呈现
- c++ - Libtins TCP流丢失数据包?
- r - 拆分具有不同行数的列,包括标题
- sql - 从 AWS S3 Parquet 文件复制数据 - 仅限于几行
- google-cloud-platform - Terraform:Cloud Run 服务上的 Cloud Endpoints?
- spring-batch - 带有 Rest API 的 Spring Batch 启动/停止
- mysql - 尝试使用带有 sql like 关键字的准备好的语句但遇到问题
- database - 两张表对一张表作为关系