繁体   English   中英

有没有办法在创建另一个 object 类型时使用 object 类型的特定值

[英]Is there a way to use specific values of an object type when creating another object type

该示例用于定义QuestionMapAnswerMap 有多种类型的问题会产生不同类型的答案。 所以,为了定义答案的 map,你必须知道这个问题。 这是一个例子:

const questionMap: QuestionMap = {
  QUESTION_1: { type: 'boolean' },
  QUESTION_2: { type: 'string' }
  QUESTION_3: { type: 'dropdown', options: { CUSTOM: 'custom answer', UNUSED: 'unused answer' }}
}
const answerMap: Answer<QuestionMap> = {
  QUESTION_1: true,
  QUESTION_2: 'custom answer'
}

在对AnswerMap类型的最佳尝试中,它不会将Answer类型用于特定问题。

type AnswerMap<TQuestionMap extends QuestionMap> = Partial<Record<QuestionId, Answer<TQuestionMap[QuestionId]>>>

根据问题类型正确键入每个答案的AnswerMap的正确定义是什么?

我要猜测您的QuestionMap类型...好吧,实际上我要消除它,因为它对我的回答没有多大帮助。 首先,如果您想强制answerMap对应于 questionMap 中的特定questionMap ,则根本不应该注释questionMap 这样做最终会将questionMap的类型扩大到完全忘记这些细节的东西,就像说const str: string = "hello"会忘记str是字符串文字"hello"并且只记住它是一个string .

事实上,我建议不要使用注释,而是使用const断言,以便编译器跟踪其中的每个字符串文字:

const questionMap = {
    QUESTION_1: { type: 'boolean' },
    QUESTION_2: { type: 'string' },
    QUESTION_3: { type: 'dropdown', options: { CUSTOM: 'custom answer', UNUSED: 'unused answer' } }
} as const;

如果您检查questionMap的类型,您会明白我的意思:

/* const questionMap: {
    readonly QUESTION_1: {
        readonly type: "boolean";
    };
    readonly QUESTION_2: {
        readonly type: "string";
    };
    readonly QUESTION_3: {
        readonly type: "dropdown";
        readonly options: {
            readonly CUSTOM: "custom answer";
            readonly UNUSED: "unused answer";
        };
    };
} */

所以现在编译器对questionMap有足够的了解来处理它。 但是应该做什么处理呢?


我将假设您有一些“简单”问题类型,其中问题被指定为{type: "someString"}并且答案是取决于"someString"的单一类型。 让我们告诉编译器它们:

type SimpleQuestionTypes = {
    boolean: boolean;
    string: string;
    number: number;
}

然后我要说Question类型要么是其中之一,要么是需要指定一些options"dropdown"类型:

type Question = { type: keyof SimpleQuestionTypes } | { type: "dropdown", options: any };

这对于您的用例可能不够严格,并且您可能还有其他问题类型,但这至少对于您的示例代码应该足够了。


好的,现在让我们弄清楚如何将类型T扩展Question并将其转换为预期的答案类型:

type Answer<T extends Question> =
    T['type'] extends keyof SimpleQuestionTypes ? SimpleQuestionTypes[T['type']] :
    'options' extends keyof T ? T['options'][keyof T['options']] :
    never;

如果T具有对应于其中一种简单问题类型的type属性,我们将只从SimpleQuestionTypes中读取答案类型。 否则,如果有options属性,我们将获取该options属性的所有属性值类型的并集。

最后, AnswerMap只是将Answer计算映射到 object 的每个属性上:

type AnswerMap<T extends Record<keyof T, Question>> = {
    [K in keyof T]: Answer<T[K]>
} // extends infer O ? { [K in keyof O]: O[K] } : never;
// uncomment the above if you want to see easier-to-read output types

现在, answerMap的类型应该是AnswerMap<typeof questionMap> ,我们在这里使用 TypeScript typeof类型查询运算符来获取确切类型的questionMap 像这样:

const answerMap: AnswerMap<typeof questionMap> = {
    QUESTION_1: true,
    QUESTION_2: 'str',
    QUESTION_3: 'custom answer'
}

顺便说一句,如果我们检查这个类型,它相当于如下:

/* const answerMap: {
    readonly QUESTION_1: boolean;
    readonly QUESTION_2: string;
    readonly QUESTION_3: "custom answer" | "unused answer";
} */

这大概就是你想要的。 为了确定起见,让我们看看当您为answerMap提供错误值时会发生什么失败:

const badAnswerMap1: AnswerMap<typeof questionMap> = { // error!
    QUESTION_1: true,
    QUESTION_2: 'str',
}; // Property 'QUESTION_3' is missing

const badAnswerMap2: AnswerMap<typeof questionMap> = {
    QUESTION_1: true,
    QUESTION_2: 'str',
    QUESTION_3: 'custom answer',
    QUESTION_4: 'str' // error! object literal may only specify known properties
}

const badAnswerMap3: AnswerMap<typeof questionMap> = {
    QUESTION_1: "oops", // error! string is not assignable to boolean
    QUESTION_2: 'str',
    QUESTION_3: 'custom answer',
}

const badAnswerMap4: AnswerMap<typeof questionMap> = {
    QUESTION_1: true,
    QUESTION_2: 'str',
    QUESTION_3: 'custom anwser', // error! "custom anwser" is not assignable to "custom answer" | "unused answer"
}

在我看来是对的。 希望这足以帮助您继续进行实际用例。

Playground 代码链接

暂无
暂无

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

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