使用 TypeScript 显式键入对象 [英] Explicitly typing an object with TypeScript

查看:40
本文介绍了使用 TypeScript 显式键入对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将我的小库从 JavaScript 转换为 TypeScript,我在那里有一个函数

I'm working on converting my little library from JavaScript to TypeScript, and I have a function there

function create(declarations: Declarations) {

现在声明是一个对象,其键可以是两种类型:

Now the declaration is an object the keys of which can be of 2 types:

  • 如果键是这些字符串之一:onMemeber/onCollection,那么值应该是一个数字
  • 对于任何其他字符串键,值应该是字符串
  • If the keys are one of those strings: onMemeber / onCollection, then the value should be a number
  • For any other string keys the value should be a string

这可以用 TypeScript 强制执行吗?我应该如何定义我的 Declarations 接口?

Is that possible to enforce with TypeScript? How should I define my Declarations interface?

推荐答案

TypeScript 中没有具体的类型来表示您的 Declarations 形状.

There is no concrete type in TypeScript that represents your Declarations shape.

我将总体思路称为默认属性"类型.(要求此的 GitHub 问题是 microsoft/TypeScript#17867)您想要特定属性是一种类型,然后任何其他类型默认"为其他不兼容的类型.它就像一个 索引签名,没有所有属性必须的约束可以分配给它.

I'd call the general idea a "default property" type. (GitHub issue asking for this is microsoft/TypeScript#17867) You want specific properties to be of one type, and then any others to "default" to some other incompatible type. It's like an index signature without the constraint that all properties must be assignable to it.

(需要明确的是,不能使用索引签名:

( Just to be clear, an index signature cannot be used:

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,甚至onMemberonCollection.要使索引签名真正有效,您需要将属性类型从 string 扩大到 string |number,这可能对您不起作用.)

The index signature [k: string]: string means every property must be assignable to string, even onMember and onCollection. To make an index signature that actually works, you'd need to widen the property type from string to string | number, which probably doesn't work for you. )

有一些拉取请求使这成为可能,但看起来它们不会很快成为语言的一部分.

There were some pull requests that would have made this possible, but it doesn't look like they are going to be part of the language any time soon.

通常在 TypeScript 中,如果没有可用的具体类型,您可以使用 泛型类型 以某种方式约束.下面是我如何使 Declarations 通用:

Often in TypeScript if there's no concrete type that works you can use a generic type which is constrained in some way. Here's how I'd make Declarations generic:

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.这种自引用约束确保对于 declarations 的每个属性 K,它的类型都是 K extends 'onMember' |'onCollection' ?数字:字符串,一个条件类型,这是您所需形状的相当简单的翻译.

You can see that the declarations parameter is of type T, which is constrained to Declarations<T>. This self-referential constraint ensures that for every property K of declarations, it will be of type K extends 'onMember' | 'onCollection' ? number : string, a conditional type that is a fairly straightforward translation of your desired shape.

让我们看看它是否有效:

Let's see if it works:

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: ""} 代替.这太令人讨厌了,您可能想要使用辅助函数来允许为您推断此类类型,而不是手动注释:

Of course, using a generic type isn't without some annoyances; suddenly every value or function that you wanted to use Declarations with will need to be generic now. So you can't do const foo: Declarations = {...}. You'd need const foo: Declarations<{onCollection: number, foo: string}> = {onCollection: 1, foo: ""} instead. That's obnoxious enough that you'd likely want to use a helper function like to allow such types to be inferred for you instead of manually annotated:

// 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;
}>*/

<小时>

好的,希望有帮助;祝你好运!


Okay, hope that helps; good luck!

链接到代码

这篇关于使用 TypeScript 显式键入对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆