4

如果我有一个由键组成的打字稿类型:

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

然后我希望将密钥添加到该类型,同时保留当前密钥,我该怎么做呢?
例如,如果我想将键 'get_value1'、'get_value2'、'get_value3' 添加到类型 'objectKeys'

最后,我想要一个看起来像这样的类型:

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

无需手动定义以“get_”为前缀的键,我知道我可以输入键来创建这个对象——但这对我的用例来说是不可行的。我只是想将一些可能存在或不存在的键添加到类型“objectKeys”中

我也知道我可以创建允许任何键值的泛型或任何类型,但是我必须知道实际的键名。允许请求对象的任何键对我没有帮助,我需要现有的键+我想添加的键。

谢谢你的帮助。

为清楚起见添加:

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.

希望那更清楚等等。

4

3 回答 3

3

听起来您要求连接字符串文字类型:也就是说,您希望能够获取字符串文字"get_"和另一个类似的字符串文字"value1",并且让 TypeScript 明白,如果您连接这些类型的字符串,您会得到一个字符串类型"get_value1"。不幸的是,这个特性在 TypeScript 2.4 中不存在(并且可能在 2.5 或 2.6 中也不存在)。

所以没有办法做你所要求的并保持严格的类型安全。当然,您可以放宽类型安全性并允许访问任何未知密钥:

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

但你说你不想那样做。


在这种情况下,我提出的建议是放弃向对象添加任意键,而是让 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

这对于开发人员(obj.get.val1vs. obj.get_val1)来说并没有太大的不同,但对 TypeScript 的跟进能力有很大的影响。如果您对添加键的代码有任何控制权,我强烈建议您做一些这样的 TypeScript 友好的事情,因为如果您不需要,您不想花时间与 TypeScript 打架。


否则,如果只有类型级别的字符串连接对你有用,并且你觉得你的用例足够引人注目,也许你应该去相关的 GitHub 问题并给它一个并描述为什么它对你来说是必须的。

希望有帮助。祝你好运!

于 2017-08-31T15:54:04.550 回答
0

2020 更新

它在 typescript v4.1.0中

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

于 2021-01-21T13:52:37.000 回答
0

您可以使用模板文字。

这是一个属性id必须为#+的示例key

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

所以以下是正确的:

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

但这是不正确的:

let y: MyInterface<'foo'> = {
    something: {
        foo: { id: 'foo' }
    }
}
于 2021-05-31T16:28:28.380 回答