当字符串文本同时用作类型和运行时变量时,如何防止复制和粘贴字符串文本?



"@types/aws-lambda"中的EventBridgeEvent接口定义为:

export interface EventBridgeEvent<TDetailType extends string, TDetail>

请注意,TDetailType扩展了string类型。我感兴趣的是,我可以将变量定义为:

event: EventBridgeEvent<'stringLiteralFoo', Bar>

为了防止复制/粘贴字符串文字,我试图将其定义为变量,但后来无法将其用作类型。

const stringLiteralFooVar = 'stringLiteralFoo'
event: EventBridgeEvent<stringLiteralFooVar, Bar>

我得到的错误是:

"stringLiteralFoo"指的是一个值,但在此处用作类型。你的意思是"字符串类型LiteralFoo"吗?

事件桥事件的完整定义是

export interface EventBridgeEvent<TDetailType extends string, TDetail> {
id: string;
version: string;
account: string;
time: string;
region: string;
resources: string[];
source: string;
'detail-type': TDetailType;
detail: TDetail;
'replay-name'?: string;
}

因此,为了找到特定事件的detail-type,我正在执行以下操作:

event: EventBridgeEvent<'stringLiteralFoo', Bar>
if (event['detail-type'] == 'stringLiteralFoo') {
// logic here
}

但是我想防止复制/粘贴'stringLiteralFoo'文字。

以下是可重复性最小的示例:

export interface EventBridgeEvent<TDetailType extends string, TDetail> {
'detail-type': TDetailType;
detail: TDetail;
}
const event: EventBridgeEvent<'Foo1' | 'Foo2', Bar> = {
'detail-type': 'Foo1',
detail: {}, // of type Bar
}
if (event['detail-type'] == 'Foo1') {
// logic here
}
interface Bar {}

所以我的最后一个问题是,在上面的例子中,我如何防止复制和粘贴文本字符串;'stringLiteralFoo'

如果你不想多次提到相同的带引号的字符串作为字符串文字值和字符串文字类型,你可以这样做。首先,创建一个const断言的相关字符串文本数组:

const detailTypes = ["Foo1", "Foo2"] as const;

const断言告诉编译器,您不希望它简单地将detailTypes的类型推断为string[],这是一个由任意数量的未知字符串组成的可变无序数组。相反,您希望它推断出一个有序的、固定长度的、只读的元组类型,该类型由内容的字符串文本类型组成。因此编译器推断detailTypes:

// const detailTypes: readonly ["Foo1", "Foo2"]

有了这一点,我们可以使用各种值和类型运算符来获得您关心的类型,而无需再次写出带引号的字符串值和类型。例如:

const event: EventBridgeEvent<typeof detailTypes[number], Bar> = {
'detail-type': detailTypes[0],
detail: {}, // of type Bar
}
if (event['detail-type'] == detailTypes[0]) {
}

类型typeof detailTypes使用TypeScripttypeof类型查询运算符来解析为readonly ["Foo1", "Foo2"]。为了从中获得并集类型"Foo1" |"Foo2",我们可以使用number索引对其进行索引:typeof detailTypes[number]。您可以将其想象为:如果您使用类型为number的有效密钥索引到detailTypes,您会得到什么类型?答案是";CCD_ 21或CCD_;,从而形成联盟。

您可以编写detailTypes[0]detailTypes[1]来获取各个元素的值。如果您需要它们的类型,您可以再次使用typeof类型运算符,或者直接使用

type ElemOne = typeof detailTypes[1];
// type ElemOne = "Foo2"

或间接

const elemOne = detailTypes[1];
type ElemOneAlso = typeof elemOne;
// type ElemOneAlso = "Foo2"

好了。


请注意,在type ElemOne = typeof detailTypes[1]中,1数字文本类型,[1]indexed访问类型,而在const elemOne = detailTypes[1]中,1是<em]数值>的数字文本,[1]是JavaScript中的实际索引操作。它们看起来是一样的,在某种意义上,它们谈论的是类似的操作,但它们并不相同。解释这种差异可能很棘手。type ElemOne = ...行纯粹在类型级别上使用TypeScript类型运算符,而const elemOne = ...行在级别上使用JavaScript运算符。类型系统将从发出的JavaScript中完全删除。看看这个不同问题的答案,关于类型和值如何存在于不同的级别中,以及它们看起来如何相同,但有时指的是非常不同的东西。

游乐场链接到代码

最新更新