如何使用具有 const 断言(作为 const)的对象作为另一种类型键的唯一符号
How to use an object with const assertion (as const) as an unique symbol for another type key
我有一个路线列表:
const N: any = "N"; // get const from non typed lib for example or typed as "string"
const PREFIX = `Route_${N}` as const;
const Route = {
Route1: `${PREFIX}_1`,
Route2: `${PREFIX}_2`
} as const; // I use object litteral because enum doesn't allow computed value
我想为每条路线输入参数:
type ParamsList = {
[Route.Route1]: undefined;
[Route.Route2]: { value: string };
};
但不允许出现此 TypeScript 错误:
A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1170)
如何将我的路线名称用作我的参数列表类型的唯一符号键? const 断言 (as const
) 不应使对象键唯一?
感谢您的帮助。
新答案
让我们解压错误信息:
A computed property name in a type literal must refer to an expression whose type is a literal type is a literal type or a 'unique symbol' type.ts(1170)
“计算的 属性 名称”指的是最终 ParamsList
类型中的属性 -(例如 [Route.Route1]: undefined;
中的 Route.Route1
)。
所以打字稿说 Route.Route1
必须是最终计算为“文字类型”的表达式(例如,像 "Route1_N_1"
这样的特定字符串,或者像 42
这样的特定数字) , 或 Symbol (这里不重要,但如果你好奇,检查 the Symbol docs)
Route.Route1
当前的计算结果如下:
`Route_${any}_1`
这被称为“模板文字类型”(参见 docs)- 与文字字符串(例如 "Route_N_1"
不同 ). TS 也需要这个是有道理的——它使编译器处于这样一种情况,即可以为 ParamsList
上的相同 属性 定义两种(或更多)不同的类型,如果有多个模板文字类型可以以重叠的方式扩展。
所以如果你想解决这个问题,你需要以某种方式使 typescript 能够将 Route.Route1
计算为特定字符串,而不是通用字符串。
The docs say 模板文字类型“能够通过联合扩展成许多字符串”,这正是您想要的。为了触发这个,你必须确保所有模板位置(例如 Route_${N}
中的 ${N}
都得到特定的文字字符串类型(例如 "foo"
或 "bar"
,而不是东西像 any
或 string
).
这是一种方法:
// const N: any = "N"; // original type "any" <--this won't work
const N = "N" as const; // new type "N" <-- this will
const PREFIX = `Route_${N}` as const; // new type: "Route_N"; original type: "Route_${any}"
const Route = {
Route1: `${PREFIX}_1`, // new type: "Route_N_1"; original type: "Route_${any}_1"
Route2: `${PREFIX}_2` // new type: "Route_N_2"; original type: "Route_${any}_2"
} as const;
type ParamsList = {
[Route.Route1]: undefined;
[Route.Route2]: { value: string };
};
更新:如果您正在构建的库不提供字符串文字类型,如何向其提供字符串文字类型
在上面的示例中,我们手动创建了 N
变量,因此很容易提供带有 as const
断言的字符串文字类型。
然而,如果 N
实际上是从另一个库导入的东西,它具有 any
类型,我们将需要以某种方式自己提供这种类型。正如您在评论中指出的那样,一种方法是在末尾添加一个 as "MyString"
断言。
另一种方法是使用 module augmentation combined with declaration merging。在您在评论中提供的具体示例中,您是这样做的:
import { StyleSheet, Text, View, NativeModules } from "react-native";
const { NativeNaviagtion } = NativeModules;
// NativeNavigation.Page1 <-- this has type "any"
您可以“扩充”"react-native
”模块提供的 NativeModules 类型,以便 NativeModules.NativeNavigation.Page1
具有文字字符串类型 "Page1"
,方法是:
declare module "react-native" {
interface NativeModulesStatic {
NativeNaviagtion: { Page1: "Page1" };
}
}
有关不同上下文中的类似问题,请参阅 this codesandbox example and 。
旧答案
您的原始示例(没有 const N: any = "N"
的附加间接层)将在 TypeScript 4.1+ 中运行,但在 TypeScript 4.0- 中不起作用。那是因为TS在4.1中加入了Template Literal Type特性。
在 4.1 之前,Route
const 将具有这种类型:
const Route: {
readonly Route1: string;
readonly Route2: string;
}
在 4.1 之后,Route
const 具有这种类型:
const Route: {
readonly Route1: "Route_1";
readonly Route2: "Route_2";
}
所以对于 4.0- 版本的打字稿,这...
type ParamsList = {
[Route.Route1]: undefined;
[Route.Route2]: { value: string };
};
...将被解释为...
type ParamsList = {
[string]: undefined;
[string]: { value: string };
};
...这是不明确的,并抛出该错误。在 4.1+ 中,它被正确地解释为:
type ParamsList = {
Route_1: undefined;
Route_2: {
value: string;
};
}
我有一个路线列表:
const N: any = "N"; // get const from non typed lib for example or typed as "string"
const PREFIX = `Route_${N}` as const;
const Route = {
Route1: `${PREFIX}_1`,
Route2: `${PREFIX}_2`
} as const; // I use object litteral because enum doesn't allow computed value
我想为每条路线输入参数:
type ParamsList = {
[Route.Route1]: undefined;
[Route.Route2]: { value: string };
};
但不允许出现此 TypeScript 错误:
A computed property name in a type literal must refer to an expression whose type is a literal type or a 'unique symbol' type.ts(1170)
如何将我的路线名称用作我的参数列表类型的唯一符号键? const 断言 (as const
) 不应使对象键唯一?
感谢您的帮助。
新答案
让我们解压错误信息:
A computed property name in a type literal must refer to an expression whose type is a literal type is a literal type or a 'unique symbol' type.ts(1170)
“计算的 属性 名称”指的是最终 ParamsList
类型中的属性 -(例如 [Route.Route1]: undefined;
中的 Route.Route1
)。
所以打字稿说 Route.Route1
必须是最终计算为“文字类型”的表达式(例如,像 "Route1_N_1"
这样的特定字符串,或者像 42
这样的特定数字) , 或 Symbol (这里不重要,但如果你好奇,检查 the Symbol docs)
Route.Route1
当前的计算结果如下:
`Route_${any}_1`
这被称为“模板文字类型”(参见 docs)- 与文字字符串(例如 "Route_N_1"
不同 ). TS 也需要这个是有道理的——它使编译器处于这样一种情况,即可以为 ParamsList
上的相同 属性 定义两种(或更多)不同的类型,如果有多个模板文字类型可以以重叠的方式扩展。
所以如果你想解决这个问题,你需要以某种方式使 typescript 能够将 Route.Route1
计算为特定字符串,而不是通用字符串。
The docs say 模板文字类型“能够通过联合扩展成许多字符串”,这正是您想要的。为了触发这个,你必须确保所有模板位置(例如 Route_${N}
中的 ${N}
都得到特定的文字字符串类型(例如 "foo"
或 "bar"
,而不是东西像 any
或 string
).
这是一种方法:
// const N: any = "N"; // original type "any" <--this won't work
const N = "N" as const; // new type "N" <-- this will
const PREFIX = `Route_${N}` as const; // new type: "Route_N"; original type: "Route_${any}"
const Route = {
Route1: `${PREFIX}_1`, // new type: "Route_N_1"; original type: "Route_${any}_1"
Route2: `${PREFIX}_2` // new type: "Route_N_2"; original type: "Route_${any}_2"
} as const;
type ParamsList = {
[Route.Route1]: undefined;
[Route.Route2]: { value: string };
};
更新:如果您正在构建的库不提供字符串文字类型,如何向其提供字符串文字类型
在上面的示例中,我们手动创建了 N
变量,因此很容易提供带有 as const
断言的字符串文字类型。
然而,如果 N
实际上是从另一个库导入的东西,它具有 any
类型,我们将需要以某种方式自己提供这种类型。正如您在评论中指出的那样,一种方法是在末尾添加一个 as "MyString"
断言。
另一种方法是使用 module augmentation combined with declaration merging。在您在评论中提供的具体示例中,您是这样做的:
import { StyleSheet, Text, View, NativeModules } from "react-native";
const { NativeNaviagtion } = NativeModules;
// NativeNavigation.Page1 <-- this has type "any"
您可以“扩充”"react-native
”模块提供的 NativeModules 类型,以便 NativeModules.NativeNavigation.Page1
具有文字字符串类型 "Page1"
,方法是:
declare module "react-native" {
interface NativeModulesStatic {
NativeNaviagtion: { Page1: "Page1" };
}
}
有关不同上下文中的类似问题,请参阅 this codesandbox example and
旧答案
您的原始示例(没有 const N: any = "N"
的附加间接层)将在 TypeScript 4.1+ 中运行,但在 TypeScript 4.0- 中不起作用。那是因为TS在4.1中加入了Template Literal Type特性。
在 4.1 之前,Route
const 将具有这种类型:
const Route: {
readonly Route1: string;
readonly Route2: string;
}
在 4.1 之后,Route
const 具有这种类型:
const Route: {
readonly Route1: "Route_1";
readonly Route2: "Route_2";
}
所以对于 4.0- 版本的打字稿,这...
type ParamsList = {
[Route.Route1]: undefined;
[Route.Route2]: { value: string };
};
...将被解释为...
type ParamsList = {
[string]: undefined;
[string]: { value: string };
};
...这是不明确的,并抛出该错误。在 4.1+ 中,它被正确地解释为:
type ParamsList = {
Route_1: undefined;
Route_2: {
value: string;
};
}