![](/img/trans.png)
[英]Convert specific types of values in an object to another type in TypeScript
[英]Is there a way to use specific values of an object type when creating another object type
该示例用于定义QuestionMap
和AnswerMap
。 有多种类型的问题会产生不同类型的答案。 所以,为了定义答案的 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"
}
在我看来是对的。 希望这足以帮助您继续进行实际用例。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.