如何在Typescript中构建和使用文本作为对象键?

wbrvyc0a  于 2023-03-04  发布在  TypeScript
关注(0)|答案(1)|浏览(126)

我想创建一个Typescript帮助器类型来提高类型安全性。我想让Typescript将对象键作为文本来“计算”和检查。
目前我有WordCard类型,但是Word类型中的字段很难看,没有真正进行类型检查。

type Card = {
    word_1: Word
    word_2: Word
}

type Word = {
    // ... other stuff

    in_Cards_as_word_1: Card[]
    in_Cards_as_word_2: Card[]
}

编辑:使用@jcalz的LiteralOf实现:

interface TypeMap {
    Card: Card;
    Date: Date;
}
type LiteralOf<T> = { [K in keyof TypeMap]: T extends TypeMap[K] ? K : never }[keyof TypeMap];

是否可以像下面这样使用类型检查过的helper InAs

type InAs<T extends LiteralOf<T>, F extends keyof T> = `in_${LiteralOf<T>}_as_${F}`

type Word = {
    // ... other stuff

    InAs<Card, 'word_1'>: Card[]
    InAs<Card, 'word_2'>: Card[]
}

const word: Word
// this should be valid, because `InAs` uses `LiteralOf` and 
// something like a literal interpolation under the hood
word.in_Card_as_word_1
jckbn6z7

jckbn6z71#

最后一种通用方法是假设对于给定的类型T,我们总是要迭代它的所有属性,并且属性类型将是T[]

type InAsT<T> = { [K in Exclude<keyof T, symbol> as InAs<T, K>]: T[] }

type WordT = {
    //... other stuff
} & InAsT<Card>
/* type WordT = {
    in_Card_as_word_1: Card[];
    in_Card_as_word_2: Card[];
} */

这里InAsT<T>使用Map类型中的键重Map将每个键KT的键转换为InAs<T, K>
更一般的做法是允许将属性值类型指定为V,而不是假定为T[]

type InAsTV<T, V> = { [K in Exclude<keyof T, symbol> as InAs<T, K>]: V }

type WordTV = {
    //... other stuff
} & InAsTV<Card, Card[]>
/* type WordTV = {
    in_Card_as_word_1: Card[];
    in_Card_as_word_2: Card[];
} */

我能想到的最通用的解决方案是允许输出的每个属性被单独指定,这并没有一个 * great * 语法;每个属性都需要一个三元组类型T,它的键之一K,以及结果属性的预期值类型V,所以也许它应该取元组类型的并集,如下所示:

type InAsTKV<TKV extends [any, string | number, any] & C,
    C = TKV extends [infer T, any, any] ? [T, keyof T, any] : never> =
    { [U in TKV as InAs<U[0], U[1]>]: U[2] }

type WordTKV = {
    // ... other stuff
} & InAsTKV<
    | [Card, 'word_1', Card[]]
    | [Card, 'word_2', Card[]]
>
/* type WordTKV = {
    in_Card_as_word_1: Card[];
    in_Card_as_word_2: Card[];
} */

这基本上是相同的操作,只是我们不是迭代T的每个键,而是迭代TKV的每个联合成员,在作为键值输出之前,我们将其拆分为TKV
我还做了一个约束C,以确保对于TKV的每个成员,K元素都是T元素的键,它(ab)使用一个泛型类型参数default来做这件事,以避免循环警告(否则TKV以编译器不喜欢的方式直接依赖于它自己)。
无论如何,这取决于您的用例,您希望采用其中的一种方法(或这些方法的组合)。
Playground代码链接

相关问题