简体   繁体   English

在打字稿中合并类型(将键添加到现有的 keyof 类型)

[英]Merging types in typescript (adding keys to an existing keyof type)

If I have a typescript type consisting of keys:如果我有一个由键组成的打字稿类型:

const anObject = {value1: '1', value2: '2', value3: '3'}
type objectKeys = keyof typeof anObject

and then I wish to add keys to that type, while retaining the current keys, how do I go about doing that?然后我希望为该类型添加键,同时保留当前键,我该怎么做?
for example, if I wanted to add the keys 'get_value1', 'get_value2', 'get_value3' to the type 'objectKeys'例如,如果我想将键“get_value1”、“get_value2”、“get_value3”添加到“objectKeys”类型

In the end, I want a type that looks like so:最后,我想要一个看起来像这样的类型:

type objectKeys = keyof anObject + 'get_value1', 'get_value2', 'get_value3'

without having to manually define the keys prefixed with 'get_', I understand that I can type out the keys to create this object - however that is not feasible for my use case.无需手动定义以“get_”为前缀的键,我知道我可以输入键来创建这个对象——但这对我的用例来说是不可行的。 I simply want to add some keys that may or may not exist to the type 'objectKeys'我只是想添加一些可能存在或可能不存在的键到类型“objectKeys”

I am also aware that I can create a generic or any type that allows for any key value, however I must know the actual key names.我也知道我可以创建一个泛型或任何允许任何键值的类型,但是我必须知道实际的键名。 It does not help me to allow for ANY key to be requested of the object, I need the existing keys + the ones I'd like to add.允许请求对象的任何键对我没有帮助,我需要现有的键 + 我想添加的键。

Thanks for any help.感谢您的帮助。

added for clarity:为清楚起见添加:

const anObject = {val1: '1', val2: '2'}
type objectKeys = keyof typeof anObject

Object.keys(anObject).forEach(key => {
  const getAddition = `get_${key}`
  anObject[getAddition] = getAddition
})

// now I don't know whats next, how do I update objectKeys to include the 
// additions added in the forEach loop.

// What I really want is to not have to add the 'get' values to the object 
// at all, JUST to the type.  I want typechecking for the get values that 
// may or may not actually exist on the object.

hope thats clearerer and such.希望那更清楚等等。

It sounds like you're asking for concatenation of string literal types : that is, you want to be able to take the string literal "get_" and another string literal like "value1" , and have TypeScript understand that if you concatenate strings of those types you get a string of the type "get_value1" .听起来您要求串联字符串文字类型:也就是说,您希望能够采用字符串文字"get_"和另一个字符串文字,如"value1" ,并且让 TypeScript 了解如果您将这些字符串串联起来类型你得到一个类型为"get_value1"的字符串。 Unfortunately, this feature does not exist as of TypeScript 2.4 (and probably won't exist in 2.5 or 2.6 either) 🙁.不幸的是,这个功能在 TypeScript 2.4 中不存在(并且可能在 2.5 或 2.6 中也不存在)🙁。

So there's no way to do what you're asking for and maintain strict type safety.所以没有办法做你所要求的并保持严格的类型安全。 You can, of course, relax the type safety and allow access for any unknown key:当然,您可以放宽类型安全并允许访问任何未知密钥:

const anObject = {val1: '1', val2: '2'};
const openObject: { [k: string]: any } & typeof anObject = anObject;
// replace "any" above with whatever type the get_XXX values are

Object.keys(openObject).forEach(key => {
  const getAddition = `get_${key}`
  openObject[getAddition] = getAddition
})
openObject.val1 = 1; // error, val1 is known to be a string
openObject.get_val1 = 1; // no error, get_val1 is any
openObject.gut_val4 = 1; // no error, oops, sorry

but you said you don't want to do that.但你说你不想那样做。


In that case, the suggestion I'd make is to give up on adding arbitrary keys to the object, and instead make the getters (or whatever they are) hang off a single get property, like so:在这种情况下,我的建议是放弃向对象添加任意键,而是让 getter(或任何它们)挂在单个get属性上,如下所示:

const anObject = { val1: '1', val2: '2' }

type AnObject = typeof anObject;
type ObjectKeys = keyof AnObject;
type GetAugmentedObject = AnObject & { get: Record<ObjectKeys, any> }; 
// replace "any" above with whatever type the get.XXX values are 

const get = {} as GetAugmentedObject['get'];
Object.keys(anObject).forEach((key: ObjectKeys) => get[key] = key);
const augmentedObject: GetAugmentedObject = { ...anObject, get }

augmentedObject.val1; // ok
augmentedObject.val2; // ok 
augmentedObject.get.val1; // ok
augmentedObject.get.val2; // ok
augmentedObject.get.val3; // error, no val3
augmentedObject.git.val1; // error, no git

This is not very different for the developer ( obj.get.val1 vs. obj.get_val1 ) but makes a big difference to TypeScript's ability to follow along.这对开发人员来说并没有太大的不同( obj.get.val1obj.get_val1 ),但对 TypeScript 的跟进能力有很大的不同。 If you're in any control of the code that's adding the keys I strongly advise doing something TypeScript-friendly like this, since you don't want to spend your time fighting with TypeScript if you don't have to.如果您对添加键的代码有任何控制权,我强烈建议您像这样做一些 TypeScript 友好的事情,因为如果您不需要的话,您不想花时间与 TypeScript 作斗争。


Otherwise, if only string concatenation at the type level will work for you, and you feel your use case is compelling enough, maybe you should go to the relevant GitHub issue and give it a 👍 and describe why it's a must-have for you.否则,如果只有类型级别的字符串连接对你有用,并且你觉得你的用例足够引人注目,也许你应该去相关的 GitHub 问题并给它一个 👍 并描述为什么它对你来说是必不可少的。

Hope that helps.希望有帮助。 Good luck!祝你好运!

2020 Update 2020 更新

It's there in typescript v4.1.0它在打字稿v4.1.0 中

You probably need this - https://github.com/microsoft/TypeScript/pull/40336你可能需要这个 - https://github.com/microsoft/TypeScript/pull/40336

You can use template literals.您可以使用模板文字。

Here's an example where the property id must be # + key :这是一个示例,其中属性id必须是# + key

interface MyInterface<K extends string> {
    something: {
        [key in K]: { id: `#${key}` }
    }
}

So the following is correct:所以以下是正确的:

let x: MyInterface<'foo'> = {
    something: {
        foo: { id: '#foo' }
    }
}

But this is incorrect:但这是不正确的:

let y: MyInterface<'foo'> = {
    something: {
        foo: { id: 'foo' }
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM