TypeScript แปลงคีย์จาก camelCase เป็น snake_case
ลองจินตนาการว่าเรามีประเภทบางอย่างสำหรับวัตถุที่มีคุณสมบัติแบบ camelCase:
type Foo = {
propertyBob: string;
propertyJane?: number;
}
มีวิธีใดบ้างในการสร้าง generic ที่จะแปลง camelCase เป็น snake_case เช่น
type foo_snake = Snakeify<Foo>;
/*
{
property_bob: string;
property_jane?: number;
}
*/
คำตอบ #1
ตอนนี้ TypeScript มีชนิดของ template literal ดังนั้นสิ่งนี้จึงเป็นไปได้จริง ๆ
แพ็กเกจ type-fest ใช้เพื่อทำตัวแปลงหลายตัวสำหรับ snake_case, kebab-case, camelCase เป็นต้น
ตัวแปลงเฉพาะนี้ตรงข้ามกับสิ่งที่คุณต้องการ แต่เป็นสิ่งที่ง่ายที่สุดในการใช้งาน:
type SnakeCaseToPascalCase<S extends string> =
S extends `${infer A}_${infer B}`
? `${Capitalize<A>}${SnakeCaseToPascalCase<B>}`
: Capitalize<S>
const snakeToPascal =
<S extends string>(s: S): SnakeCaseToPascalCase<S> =>
s.split('_')
.map(word => word[0].toUpperCase() + word.slice(1))
.join("") as any
const pascalCase = snakeToPascal("get_abstract_singleton_manager")
// pascalCase: "GetAbstractSingletonManager"
จากนั้นคุณสามารถใช้ตัวแปลงนี้ร่วมกับชนิดที่ทำการแมปได้:
type SnakeToPascal<M extends Record<string, any>> =
{[K in keyof M as SnakeCaseToPascalCase<K & string>]: M[K]}
type User = SnakeToPascal<{
first_name: string,
last_name: string,
}>
// inferred as: {
// FirstName: string;
// LastName: string;
// }
คำตอบ #2
ถ้าคุณอยากเจาะลึกลงไป ผมขอแนะนำ: https://type-level-typescript.com/
แต่ที่นี่คือคำตอบสำหรับคำถามของคุณ:
type Foo = {
propertyBob: string;
propertyJane?: number;
};
type CamelToSnakeCase<S extends string> = S extends `${infer T}${infer U}`
? `${T extends Capitalize<T> ? "_" : ""}${Lowercase<T>}${CamelToSnakeCase<U>}`
: S;
type SnakeCaseFoo = {
[K in keyof Foo as CamelToSnakeCase<K>]: Foo[K];
};
const snakeCaseFooExample: SnakeCaseFoo = {
property_bob: "hello",
property_jane: 123,
};
โอเค ดังนั้นนี่คือสิ่งที่กำลังเกิดขึ้น:
ประเภท SnakeCaseFoo เป็นวัตถุ คีย์ของวัตถุนี้ ซึ่งเป็นประเภททั่วไปที่เราเรียกตามอำเภอใจว่า K ต้องอยู่ในคีย์ของประเภท Foo นั่นคือเหตุผลที่เราทำ K in keyof Foo
ตอนนี้ กุญแจนี้ เราจะเปลี่ยนเป็นบางสิ่งอย่างอื่น เราจะเปลี่ยนกุญแจนั้นเป็น camel_case
ประเภทของคีย์นั้นคือประเภทใดก็ตามที่คีย์นั้นมีใน foo ดังนั้นจึงเป็น Foo[K] ดังนั้น ถ้าประเภทของคีย์นั้นใน Foo เป็นสตริง ประเภทใหม่ของคีย์นั้นใน SnakeCaseFoo ของเราก็จะเป็นสตริงด้วยเช่นกัน
ตกลง ตอนนี้เรามาเปลี่ยนไปที่ประเภท CamelToSnakeCase:
ประเภทนี้ได้รับเจนเนอริก ซึ่งเรากำหนดชื่อแบบสุ่มว่า S เจนเนอริกนี้มีข้อจำกัด มันต้องสืบทอดมาจาก string (สตริงใดก็ได้) หรือประเภทที่ถูกรวมอยู่ในประเภท string เช่น ตัวอักษรสตริง 'asdf'
จากนั้นเราจะทำ Conditional Type ซึ่งมีลักษณะดังนี้:
type TrueOrFalse = A extends B ? true : false;
นี่คล้ายกับตัวดำเนินการ ternary ใน JavaScript แต่ไม่เหมือนกัน มันเป็นคุณสมบัติของ TypeScript สำหรับชนิดข้อมูล อีกครั้ง แหล่งข้อมูลที่ยอดเยี่ยมในการเรียนรู้เพิ่มเติมเกี่ยวกับมันคือ https://type-level-typescript.com/
ตอนนี้ เรากำลังจะทำการจับคู่รูปแบบบนสตริงลิตเทอรัล การจับคู่รูปแบบของเราจะใช้ตัวอักษรหนึ่งตัว ซึ่งเราเรียกว่า T และจากนั้นก็ส่วนที่เหลือของสตริง ซึ่งเราเรียกว่า U
จากนั้นเราจะพูดว่า: ถ้า S ไม่ตรงตามรูปแบบของเรา ประเภทผลลัพธ์ของเราก็คือ S เราไม่ทำการเปลี่ยนแปลงมัน แต่ถ้า S ตรงตามรูปแบบของเรา เราจะคืนค่าประเภทใหม่ หนึ่งประเภทที่เป็นการแปลงของ S นั้น
ดังนั้นมาลองแปลง S ซึ่งประกอบด้วยตัวอักษร T และส่วนที่เหลือของสตริง U:
ถ้าตัวอักษร T เป็นตัวพิมพ์ใหญ่ (T extends Capitalize<T>) เราจะคืนค่า '_' มิฉะนั้นเราจะคืนค่า '' ตามด้วยตัวอักษร T นั้นในรูปตัวพิมพ์เล็ก ถ้ามันเป็นตัวพิมพ์เล็กอยู่แล้วก็จะไม่ทำอะไรกับ T ดังนั้นเราจึงมีตัวอย่างต่อไปนี้สำหรับส่วนนี้โดยเฉพาะ:
if T is 'a' then it is converted to ''+'a' which is the same as 'a'.
if T is 'A' then it is converted to '_'+'a' which is '_a'
จากนั้นมันจะเรียกตัวเองซ้ำด้วยตัวอักษรที่เหลือ (U)
ดังนั้น สำหรับสตริง ptBob การคำนวณจะทำดังนี้:
CamelToSnakeCase<'ptBob'>
Tคือ 'p' และUคือ 'tBob' => 'p' + CamelToSnakeCase<'tBob'>Tคือ 't' และUคือ 'Bob' => 't' + CamelToSnakeCase<'Bob'>Tคือ 'B' และUคือ 'ob' => '_b' + CamelToSnakeCase<'ob'>Tคือ 'o' และUคือ 'b' => 'o' + CamelToSnakeCase<'b'>Tคือ 'b' และUคือ '' => 'b' + CamelToSnakeCase<''>Sไม่สอดคล้องกับ$ {infer T}$ {infer U}ดังนั้น =>Sซึ่งในจุดนี้คือ''
ขึ้นไปบนกอง เราจะได้ 'p' + 't' + '_b' + 'o' + 'b' ซึ่งคือ 'pt_bob'.
เราสามารถเปลี่ยนประเภทตัวอักษรแบบ string literal จาก 'ptBob' เป็นประเภทตัวอักษรแบบ string literal 'pt_bob' ได้สำเร็จ
