记录:仅限于带有键入项的现有键

时间:2019-11-24 13:11:57

标签: typescript

我有以下代码:

appendTo="@(body)"

此结构允许我将新条目添加到export const myRecord = { Entry1: { title: 'hey ho', data: { version: 1, valid: true } }, Entry2: { title: 'hey hey', data: { version: 2, valid: false } } } as const; export type MyRecordsIds = keyof typeof myRecord; export function doSomething(recordId: MyRecordsIds){ //does something } ,而无需将它们显式添加到myRecord
反过来,MyRecordIds类型允许我在代码中限制这些ID。

到目前为止,一切都很好。但是现在我想将MyRecordsIds中的值限制为某种类型,例如:

myRecord

最明显的解决方法是:

export interface Entry {
  title: string;
  data: {
    version: number;
    valid: boolean;
  }
}

该限制有效,但我放弃了对export const myRecord: Record<string, Entry> = { Entry1: { title: 'hey ho', data: { version: 1, valid: true } }, Entry2: { title: 'hey hey', data: { version: 2, valid: false } } }; 类型的限制(由于MyRecordsIds,它变为string)。

我到目前为止得到的最接近的是:

Record<string,Entry>

这同时限制了记录条目和ID,这意味着在以下情况下构建会失败:

  1. 我正在尝试将const myRecord = { Entry1: { title: 'hey ho', data: { version: 1, valid: true } }, Entry2: { title: 'hey hey', data: { version: 2, valid: false } } } as const; type MyRecordsIds = keyof typeof myRecord; export const myRecordTyped: Record<MyRecordsIds, Entry> = myRecord; 与不是记录中键的某些字符串一起使用。
  2. 我正在尝试添加不符合doSomething类型的条目。

但是,这种方法有一个严重的缺点。如果其中一项输入错误,则构建将在分配时失败(而不是指向记录中有问题的行),这将使得难以跟踪问题:

Entry

在保持对记录条目类型和记录ID的限制的同时,是否有更好的方法来实现此行为?

2 个答案:

答案 0 :(得分:3)

也许您只是希望一个辅助函数可以让您定义myRecord,使其可以接受任何键,但将值限制为Entry

const asMyRecord = <T extends Record<keyof T, Entry>>(t: T): Record<keyof T, Entry> => t;

您将像这样使用它:

export const myRecord = asMyRecord({
    Entry1: {
        title: 'hey ho',
        data: {
            version: 1,
            valid: true
        }
    },
    Entry2: {
        title: 'hey hey',
        data: {
            version: 2,
            valid: false
        }
    }
});

如果您尝试将任何属性值设置为不可分配给Entry的内容,则会收到错误消息:

const badMyRecord = asMyRecord({
    EntryNope: {
        title: 123, // error! number is not strng
        data: {
            version: "one", // error! string is not number
            valid: false
        }
    }
})

已知MyRecord像以前一样具有特定的文字键:

type MyRecordsIds = keyof typeof myRecord; // "Entry1" | "Entry2"

因此,它应该保持合理的强类型(当前asMyRecord最终会产生类型为Record<keyof T, Entry>的值):

myRecord.Entry2.title = "okay"; // okay
myRecord.Entry2.oops = false; // error! "oops" doesn't exist on Entry
myRecord.Entry3.title = "nope";  // error! "Entry3" not "Entry1" | "Entry2"

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

Link to code

答案 1 :(得分:0)

我能得到的最接近的结果是将每个条目分配给键入常量

export interface Entry {
  title: string
  data: {
    version: number
    valid: boolean
  }
}

const Entry1: Entry = {
  title: 'hey ho',
  data: {
    version: 1,
    valid: true
  }
}

const Entry2: Entry = {
  title: 'hey hey',
  data: {
    version: 2,
    valid: false
  }
}

const myRecord = {
  Entry1,
  Entry2
}

type MyRecordsIds = keyof typeof myRecord

export const myRecordTyped: Record<MyRecordsIds, Entry> = myRecord