繁体   English   中英

如何使用 TypeScript 深度推断类型?

[英]How to deeply infer types using TypeScript?

我目前正在与某种类型推断作斗争。

当前 State

目前,我的 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>;
}

目标 State

我现在的目标是,我希望能够以非常严格的方式输入 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也必须是通用的,其中questionsT并且calculatectxCalculateCtx<T>

对于CalculateCtx<T> ,我们使用T的键来构造带有新Recordanswers形状。

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.

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