2024-05-30.md
π‘DIL: μ΄νν°λΈ νμ μ€ν¬λ¦½νΈ
μ€ν°λ: μκ° CS, https://github.com/monthly-cs/2024-05-effective-typescript
μμ±μΌ: 2024-05-30
μμ±μ: dusunax
μμ΄ν 33: string νμ λ³΄λ€ λ ꡬ체μ μΈ νμ μ¬μ©νκΈ° Prefer More Precise Alternatives to String Types
λΆλΆλͺ ν string νμ μ¬μ©
- λ¬Έμμ΄μ λ¨λ°νμ¬ μμ±λμλ€. stringly typed.
interface Album {
artist: string;
title: string;
releaseDate: string; // YYYY-MM-DD
recordingType: string; // E.g., "live" or "studio"
// μλͺ»λ κ°μ μ
λ ₯ν΄λ, νμ
μ€ν¬λ¦½νΈκ° μ μ μμ΄ μ€λ₯λ‘ λλ¬λμ§ μμ
}
// κ°μ
/** λ
Ήμμ΄ μ§νλ νκ²½ */
type RecordingType = "studio" | "live";
// νμ
μ λͺ
μμ μΌλ‘ μ μ
// ν¨μλ₯Ό μ¬μ©νλ κ³³μμ νμ
μ μ€λͺ
μ λ³Ό μ μλ€
interface Album {
artist: string;
title: string;
releaseDate: Date;
recordingType: RecordingType;
}
- λ€λ₯Έ κ³³μΌλ‘ κ°μ΄ μ λ¬λμ΄λ μ½κ² νμ μ νμΈν μ μλ€.
- νμ μ μ μνκ³ , μ£Όμμ λΆμΌ μ μλ€
- keyof μ°μ°μλ‘ κ°μ²΄μ μμ± μ²΄ν¬κ° κ°λ₯νλ€.
μμ: μ λλ¦ νμ μ μ¬μ©νλ μμ
// βοΈ anyλ₯Ό μ¬μ©ν΄ μ λ°νμ§ λͺ»νλ€.
// λ°νκ°μ anyλ₯Ό μ¬μ©νλ κ²μ λ§€μ° μ’μ§ μλ€.
function pluckA(records: any[], key: string): any[] {
return records.map((r) => r[key]);
}
// βοΈ μ λλ¦ νμ
function pluckB<T>(records: T[], key: string): any[] {
return records.map((r) => r[key]);
// keyλ "artist" | "title" | "releaseDate" | "recordingType" λ§ μ ν¨νλ€.
// μ§κΈμ string.
// κ·Έλ λ€λ©΄?π€
}
// Keyμ νμ
type K = keyof Album;
// κ·Έλ¬λ―λ‘, μ€νΈλ§μ keyof Tλ‘ λ°κΎΈλ©΄ λλ€.
// βοΈ keyof T
function pluckC<T>(records: T[], key: keyof T) {
return records.map((r) => r[key]);
}
// νμ
체컀λ₯Ό ν΅κ³Όνκ³ , λ°ν νμ
μ μΆλ‘ νκ³ , μΆλ‘ λ νμ
μ μ μ μλ€.
// function pluck<T>(records: T[], key: keyof T): T[keyof T][]
// T[keyof T]λ? μμ§ λ무 λλ€. (string | Date)[]
// βοΈ λ λ²μ§Έ μ λλ¦ μΆκ°νκΈ° π
// K extends keyof T : keyof Tμ λΆλΆ μ§ν©
// keyof Tμ λ²μμ μνλ κ°μ΄λ€. λ¨μΌ κ°
function pluckD<T, K extends keyof T>(records: T[], key: K): T[K][] {
return records.map((r) => r[key]);
}
pluckD<Album, "releaseDate">([someAlbum], "releaseDate");
// Date[]
// [Date: "2024-05-30T00:00:00.000Z"]
- pluckC: μ λλ¦
- pluckD: 2κ°μ§ μ λλ¦
keyof
μ K extends keyof T
μ΄ν΄λ³΄κΈ°
- νμ μ λ€λ¦κ³Ό κ΄λ ¨λ ν€μλμ ꡬ문
β keyof ν€μλ
- keyof Tλ νμ Tμ λͺ¨λ ν€λ₯Ό λνλ΄λ νμ
{
name: string;
age: number;
}
// keyof Tλ "name" | "age"μ κ°μ μ λμΈ νμ
β K extends keyof T
- Kκ° keyof Tμ μλΈνμ μ΄μ΄μΌ ν¨μ μλ―Έ
- μ¦, Kλ Tμ ν€λ€ μ€ νλ, λλ κ·Έ μ΄μμ μ‘°ν©μΌ μ μλ€.
function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
return obj[key];
}
// Kλ Tμ ν€λ€ μ€ νλμ¬μΌ νλ―λ‘, obj[key]κ° μ ν¨νλ€.
β K: keyof T?
- μ λ€λ¦ νμ
Kκ° keyof T νμ
κ³Ό μ νν κ°μμ λͺ
μνλ € ν¨
- Kλ λ°λμ Tμ ν€λ€ μ€ νλλ§μ κ°μ§ μ μλ€.
- K: keyof Tλ μ ν¨νμ§ μμ΅λλ€. μ λλ¦ νμ μ μ νν λλ νμ extends ν€μλλ₯Ό μ¬μ©ν΄μΌ ν©λλ€. K: keyof Tλ λ¬Έλ²μ μΌλ‘ μ€λ₯!
μ λ€λ¦μ μ μ νκ² μ¬μ©νκΈ°
function pluckD<T, K extends keyof T>(records: T[], key: K): T[K][];
- 맀κ°λ³μ νμ μ΄ μ λ°ν΄μ‘κΈ° λλ¬Έμ, Albumμ ν€μ μλμμ± κΈ°λ₯μ μ 곡ν©λλ€.
- stringμ anyμ λΉμ·ν λ¬Έμ λ₯Ό κ°μ§κ³ μμ΅λλ€.
- μλͺ» μ¬μ©νλ©΄?
- 무ν¨ν κ°μ νμ©, νμ κ°μ κ΄κ³ κ°μΆ€
- μ€μ λ²κ·Έλ₯Ό μ°ΎκΈ° λͺ»νλλ‘ νμ 체컀λ₯Ό λ°©ν΄νλ€.
- μλͺ» μ¬μ©νλ©΄?
- μ νν νμ
μ μ¬μ©νλ©΄?
- μ€λ₯λ₯Ό λ°©μ§νκ³ , μ½λμ κ°λ μ±μ ν₯μ μν¨λ€.
Things to Remember
- Avoid "stringly typed" code. Prefer more appropriate types where not every string is a possibility.
- λ¬Έμμ΄μ λ¨λ°ν΄ μ μΈν μ½λλ₯Ό νΌνμ.
- stringμ λͺ¨λ λ¬Έμμ΄μ ν λΉν μ μκΈ° λλ¬Έμ, ꡬ체μ μΈ νμ μ μ¬μ©νλ κ²μ΄ μ’λ€~
- Prefer a union of string literal types to string if that more accurately describes the domain of a variable. You'll get stricter type checking and improve the development experience.
- λ¬Έμμ΄ λ¦¬ν°λ΄ νμ μ μ λμ¨μ μ¬μ©νλ κ²μ΄, νμ 체ν¬λ₯Ό λ μ격ν ν μ μκ³ μμ°μ±μ ν₯μμμΌ μ’λ€(μλ μμ±, μ€ν νμΈ)
- Prefer keyof T to string for function parameters that are expected to be properties of an object.
- κ°μ²΄μ μμ±λͺ
μ => ν¨μ 맀κ°λ³μλ‘ λ°μ λλ?
string
보λ€keyof T
λ₯Ό μ¬μ©νμ
- κ°μ²΄μ μμ±λͺ
μ => ν¨μ 맀κ°λ³μλ‘ λ°μ λλ?