網站搜尋

typescript - 基於現有類型創建新類型並轉換鍵


我正在嘗試在 TypeScript 中對接兩個系統,每個系統都有不同的格式類型(JSON - camelCase 轉成 snake_case)。我希望能夠只創建一個類型,然後從這個類型生成另一個類型。

範例:

// [camelCase sys] I am creating this type
type consumerSchemaCamelCase = {
  categoryName: string, // 👈 camelCase
}

// [snake_case sys] This other type would "infer" from the previous
type consumerSchemaCamelCase = {
  category_name: string, // 👈 snake_case
}

這個答案 👇 幾乎是我需要的,但我的實作需要對每個鍵進行更高階的字串操作。

type Transformed<BaseType> = {
  [Key in keyof BaseType as `use${Capitalize<string & Key>}`]: () => BaseType[Key]
}

這將是一種實現轉換的方法:

const remapKey = (inputKey: string): string => inputKey.replace(/[A-Z]/g, x => `_${x.toLowerCase()}`);

謝謝,

答案#1

你可以在尾端遞歸條件類型中使用模板字面量類型,將駝峰式字符串字面量類型轉換為蛇形命名版本:

type CamelToSnake<T extends string, A extends string = ""> =
    T extends `${infer F}${infer R}` ?
    CamelToSnake<R, `${A}${F extends Lowercase<F> ? "" : "_"}${Lowercase<F>}`> :
    A

這個方法的工作原理是輸出所有字符的小寫版本,並在任何小寫版本與原始字符不同的字符前加上 _(所以 A 會變成 _a,Z 會變成 _z,但 a、z、3 和 ! 保持不變)。

然後你可以使用帶有鍵重新映射的映射類型,將具有駝峰式屬性的對象轉換為具有類似蛇形命名屬性的對象:

type CamelPropsToSnakeProps<T> =
    { [K in keyof T as K extends string ? CamelToSnake<K> : K]: T[K] }

讓我們來測試一下:

type ConsumerSchemaCamelCase = {
    categoryName: string,
    base64Hash: string;
    youCanDoThisWithVeryLongPropertiesIfYouWantAndItShouldStillWorkUnlessYouHaveLikeAThousandCharactersInTheStringButThatWouldBeCrazy: number;
}

type ConsumerSchemaSnakeCase = CamelPropsToSnakeProps<ConsumerSchemaCamelCase>;
/* type ConsumerSchemaSnakeCase = {
    category_name: string;
    base64_hash: string;
    you_can_do_this_with_very_long_properties_if_you_want_and_it_should_still_work_unless_you_have_like_a_thousand_characters_in_the_string_but_that_would_be_crazy: number;
} */

看起來不錯!

程式碼的 Playground 連結

答案#2

關於將 ConsumerSchemaCamelCase 的鍵轉換為 ConsumerSchemaSnakeCase:您可以創建一個遞歸條件輔助類型,然後使用映射類型來映射所有鍵。

TypeScript 遊樂場

type CamelCaseToSnakeCase<T extends string> = T extends `${infer U}${infer V}` ?
  `${U extends Capitalize<U> ? "_" : ""}${Lowercase<U>}${CamelCaseToSnakeCase<V>}` : T;

type ConsumerSchemaCamelCase = {
  categoryName: string,
}

type ConsumerSchemaSnakeCase = {
  [K in CamelCaseToSnakeCase<keyof ConsumerSchemaCamelCase>]: string
}