typescript - 使用 TypeScript 显式键入对象
问题描述
我正在将我的小库从 JavaScript 转换为 TypeScript,我在那里有一个函数
function create(declarations: Declarations) {
现在声明是一个对象,其键可以是 2 种类型:
- 如果键是这些字符串之一:
onMemeber
/onCollection
,那么值应该是一个数字 - 对于任何其他字符串键,值应该是一个字符串
这可以用 TypeScript 强制执行吗?我应该如何定义我的Declarations
界面?
解决方案
TypeScript 中没有代表你的Declarations
形状的具体类型。
我将一般概念称为“默认属性”类型。(要求此问题的 GitHub 问题是microsoft/TypeScript#17867)您希望特定属性为一种类型,然后将任何其他属性“默认”为其他不兼容的类型。它就像一个 索引签名,没有所有属性都必须分配给它的约束。
(为了清楚起见,不能使用索引签名:
type BadDeclarations = {
onMember: number, // error! number not assignable to string
onCollection: number, // error! number not assignable to string
[k: string]: string
};
索引签名[k: string]: string
意味着每个属性都必须可分配给string
、 evenonMember
和onCollection
。要制作真正有效的索引签名,您需要将属性类型从string
to扩大string | number
,这可能对您不起作用。)
有一些拉取请求可以使这成为可能,但看起来它们不会很快成为语言的一部分。
通常在 TypeScript 中,如果没有有效的具体类型,您可以使用以某种方式受到约束的泛型类型。以下是我如何制作通用的:Declarations
type Declarations<T> = {
[K in keyof T]: K extends 'onMember' | 'onCollection' ? number : string
};
这是create()
L的签名
function create<T extends Declarations<T>>(declarations: T) {
}
可以看到declarations
参数的类型是T
,被约束为Declarations<T>
。这种自引用约束确保对于 的每个属性K
,declarations
它将是 type K extends 'onMember' | 'onCollection' ? number : string
,这是一种条件类型,是您所需形状的相当直接的转换。
让我们看看它是否有效:
create({
onCollection: 1,
onMember: 2,
randomOtherThing: "hey"
}); // okay
create({
onCollection: "oops", // error, string is not assignable to number
onMember: 2,
otherKey: "hey",
somethingBad: 123, // error! number is not assignable to string
})
这在我看来是合理的。
当然,使用泛型类型并非没有一些麻烦。突然之间,您想要使用Declarations
的每个值或函数现在都需要是通用的。所以你不能这样做const foo: Declarations = {...}
。你需要const foo: Declarations<{onCollection: number, foo: string}> = {onCollection: 1, foo: ""}
。这太令人讨厌了,您可能希望使用一个辅助函数,例如允许为您推断此类类型,而不是手动注释:
// helper function
const asDeclarations = <T extends Declarations<T>>(d: T): Declarations<T> => d;
const foo = asDeclarations({ onCollection: 1, foo: "a" });
/* const foo: Declarations<{
onCollection: number;
foo: string;
}>*/
好的,希望有帮助;祝你好运!
推荐阅读
- nginx - 为什么 gunicorn 在 NGINX 后面运行一个 Cherrypy 应用程序时不回复 CloudFoundry 健康检查?
- javascript - 为什么使用 Tabulator 时我的数据发送为未定义?
- java - sqlite 使用外键获取数据:无法在索引 1 处绑定参数,因为索引超出范围。该语句有 0 个参数
- c++ - CMake 在 Mac 上失败
- r - 将 ISO 8601 从字符转换为 posixct 时出现奇怪的错误
- haskell - Liquid Haskell:证明组合子的错误和谓词细化的类型
- sql - 计算 postgres 中某个值的出现次数
- r - fread 函数中“强制引入的 NA”的警告
- regex - REGEX : 从四个逗号中截取字符串
- python - 如何使用另一个列表中的布尔值屏蔽列表