簡體   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