[英]How to deeply infer types using TypeScript?
我目前正在与某种类型推断作斗争。
目前,我的 object 看起来像这样:
const config: Config = {
calculate({ answers }) {
answers.questionOne // <-- SHOULD be allowed
answers.questionUnknown // <-- Should NOT be allowed
},
questions: {
questionOne: {
title: "Test Title",
answers: ["answer-1", "answer-2", "answer-3"],
},
},
}
对于我的类型,我有这样的Config
类型:
// The answers object looks like: { questionKey: answerKey }.
type CalculateCtx = { answers: Record<string, string> }
type Question = {
title: string;
answers: string[];
}
type Config = {
calculate(ctx: CalculateCtx): void;
questions: Record<string, Question>;
}
我现在的目标是,我希望能够以非常严格的方式输入 object。 我想要计算 function,只用可用的问题键输入answers
。 因此,计算 function 中的answers
将输入为{ questionOne: "answer-1" | "answer-2" | "answer-3" }
{ questionOne: "answer-1" | "answer-2" | "answer-3" }
{ questionOne: "answer-1" | "answer-2" | "answer-3" }
而不是{ [key: string]: string }
。
我知道,这是可能的(因为 trpc 和stitches.js 之类的库可以做到这一点),但我发现很难找到如何正确地做到这一点。
有人可以帮帮我吗。
编辑:
如评论中所述, questionOne
的类型还不太正确。 这是解决此问题的编辑:
function useConfig<T extends Record<string, string>>(config: Config<T>){}
type CalculateCtx<T extends Record<string, string>> = { answers: {
[K in keyof T]: T[K]
}}
type Question<V extends string> = {
title: string;
answers: V[];
}
type Config<T extends Record<string, string>> = {
calculate(ctx: CalculateCtx<T>): void;
questions: {
[K in keyof T]: Question<T[K]>
}
}
在通用 function 中使用config
object 时,可以实现类似的效果。
function useConfig<T extends Record<string, Question>>(config: Config<T>){}
我们使用T
来存储questions
的内容。
type Config<T extends Record<string, Question>> = {
calculate(ctx: CalculateCtx<T>): void;
questions: T;
}
Config
也必须是通用的,其中questions
是T
并且calculate
的ctx
是CalculateCtx<T>
。
对于CalculateCtx<T>
,我们使用T
的键来构造带有新Record
的answers
形状。
type CalculateCtx<T> = { answers: Record<keyof T, string> }
现在,将配置 object 文字传递给 function 时,您将收到所需的错误。
const config = useConfig({
calculate({ answers }) {
answers.questionOne
answers.questionUnknown // Error
},
questions: {
questionOne: {
title: "Test Title",
answers: ["answer-1", "answer-2", "answer-3"],
},
},
})
一种使用'as const'的方法。 我添加Partial<>
只是为了允许“未回答” state (具有附加| undefined
的类型的副作用。
const QuestionSettings = {
questionOne: {
title: "Title 1",
answers: ["answer-1a", "answer-1b", "answer-1c"],
},
questionTwo: {
title: "Title 2",
answers: ["answer-2a", "answer-2b"],
},
} as const;
type AvailableAnsMap = {
[qName in keyof typeof QuestionSettings]:
typeof QuestionSettings[qName] extends { answers: Readonly<unknown[]> } ?
typeof QuestionSettings[qName]['answers'][number] : never
}
type Config = {
calculate(ctx: { answers: Partial<AvailableAnsMap> }): void;
questions: typeof QuestionSettings;
}
const config: Config = {
calculate({ answers }: { answers: Partial<AvailableAnsMap> }) {
answers.questionOne //"answer-1a" | "answer-1b" | "answer-1c" | undefined
answers.questionTwo //"answer-2a" | "answer-2b" | undefined
},
questions: QuestionSettings
}
let studentAns1: Partial<AvailableAnsMap> = { questionOne: 'answer-1c' };
let studentAns2: Partial<AvailableAnsMap> = { questionTwo: 'answer-2a' };
let studentAns3: Partial<AvailableAnsMap> = { questionOne: 'answer-1a', questionTwo: 'answer-2a' };
config.calculate({ answers: studentAns1 })
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.