1. ꡬ쑰μ νμ΄νμ κ΄μ μμ μ΄ν΄λ³΄κΈ°
λ€μκ³Ό κ°μ μ½λκ° μμ΅λλ€.
type Vector2D = {
x: number;
y: number;
};
type Vector3D = {
x: number;
y: number;
z: number;
};
const calculateNorm = (p: Vector2D) => {
return Math.sqrt(p.x * p.x + p.y * p.y);
};
Vector2Dμ Vector3D νμ μ΄ μμ΅λλ€. calculateNorm ν¨μλ μλλ°μ. μ΄ ν¨μλ Vector2D νμ μ 맀κ°λ³μλ₯Ό λ°μ΅λλ€. λλ¬Έμ Vector3D νμ μ 맀κ°λ³μλ₯Ό νμ©νμ§ μμμΌ ν©λλ€. νμ§λ§ ꡬ쑰μ νμ΄ν κ΄μ μμ λ³Έλ€λ©΄ λ€μμ μ½λλ λ¬Έμ κ° μμ΅λλ€. μ΄λ₯Ό ν΄κ²°νκΈ° μν΄μ μ΄λ»κ² ν΄μΌ ν κΉμ?
const vector3D = { x: 1, y: 3, z: 5 };
calculateNorm(vector3D);
1-1. 곡μ λͺ μΉμ μ¬μ©νκΈ°
1. ꡬ쑰μ νμ΄νμ κ΄μ μμ μ΄ν΄λ³΄κΈ° μμ μ΄ν΄λ³Έ μ½λμ λ¬Έμ λ₯Ό 곡μ λͺ μΉ(nominal typing)μ μ¬μ©νλ©΄ ν΄κ²°λ©λλ€.
곡μ λͺ μΉμ νμ μ μ¬μ©νκ² λ€λ κ²μ, νμ μ΄ μλλΌ κ°μ κ΄μ μμ λͺ μΉμ μ νκ² λ€λ κ²μ λλ€. μ¦, μ΄λ€ νμ μ 곡μ λͺ μΉμ κ°μΌλ‘ κ°μ§λ μμ±μ μΆκ°νμ¬ κ³΅μ λͺ μΉ κ°λ μ μ¬μ©ν μ μμ΅λλ€. μλ₯Ό λ€μ΄ 'μν(brand)'κ° λ€λ₯Έ νμ κ³Ό ꡬλΆλλ μμ±μ΄ λ©λλ€. μ λ μ΄λ° μνκ° νκ·Έλ μ λμ¨μ νκ·Έ(μλ³μ)μ κ°λ€κ³ μκ°ν©λλ€.
Vector2D νμ μ μν(brand)λ₯Ό λΆμ΄λ©΄ λ€μκ³Ό κ°μ΅λλ€.
type Vector2D = {
_brand: '2d';
x: number;
y: number;
};
κ·Έλ¬λ©΄ μ΄μ΄μ Vectro2D νμ μ κ°μ§λ κ°μ²΄λ₯Ό λ§λ€μ΄ λ³΄κ² μ΅λλ€.
const generatorVector2D = (x: number, y: number): Vector2D => {
return { x, y, _brand: '2d' };
};
const vector2D = generatorVector2D(1, 2);
Vector2D νμ μ κ°μ²΄λ₯Ό λ§λ€κΈ° μν΄μ μμΌλ‘ generatorVector2D ν¨μλ₯Ό νμ©νλ©΄ λ©λλ€. μ΄λ κ² μμ±λ κ°μ²΄λ§ calculateNorm ν¨μλ₯Ό μ¬μ©ν μ μκ² λμ΅λλ€.
1-2. μ΄λ κ² μ¬μ©νλ€λ©΄??
const vector3D = { x: 1, y: 3, z: 5, _brand: '2d' as const };
calculateNorm(vector3D);
μμ½κ²λ μλ ꡬ쑰μ νμ΄νμΌλ‘ μΈν΄ μ€λ₯κ° μμ΅λλ€. μ΄λ¬ν μ μμ μΈ μ¬μ©μ λ§μ μ μμ΅λλ€.
λ¨, 곡μ λͺ μΉμ μ¬μ©νλ©΄ λ¨μν μ€μλ₯Ό λ°©μ§ν μ μμ΅λλ€.
2. string μνννκΈ°
μ°ν μ½μμ ν¬λ£¨λ€μ μμ μ κ³Όμ μ λ°λΌ FE_, BN_, AN_ μ κ°μ μ΄λ¦ 컨벀μ μ κ°μ§λλ€. μ½μΉλ κ·Έλ₯ μ½μΉμ μ΄λ¦μ μ¬μ©ν©λλ€. λ°νμμμλ μ΄λ¬ν 컨벀μ μ ꡬλΆνμ¬ μ²΄ν¬νκ³ μ½μ΅λλ€. νμ§λ§ νμ μμ€ν μμλ μ΄λ₯Ό νλ¨νκΈ° μ΄λ ΅μ΅λλ€.
type FECrewName = string & { _brand: 'feCrew' };
type BNCrewName = string & { _brand: 'bnCrew' };
type ANCrewName = string & { _brand: 'anCrew' };
type CoachName = string & { _brand: 'coach' };
μμ κ°μ΄ string νμ μ΄λ©΄μ _brand μμ±μ κ°μ§λ κ°μ²΄λ₯Ό μκ°ν΄ 보면 μ΄λ λ§μ΄ μ λ©λλ€. μ΄λ° κ°μ²΄λ νμ μμ€ν μ μμμ λλ€. μ΄λ° μΈ‘λ©΄μμ λ³Έλ€λ©΄ νκ·Έ(μλ³μ)μλ λ€λ₯Έ λλμ λλ€.(γ γ )
2-1. μ΄λ¦ λ€μ μ΄λͺ¨μ§λ₯Ό λΆμ΄κ³ μΆμ΄...
μ΄λ° μ΄λ¦ 컨벀μ μ λ°νμΌλ‘ κ° κ³Όμ μ ν¬λ£¨λ₯Ό νλ³νλ ν¨μλ λ€μκ³Ό κ°μ΅λλ€.
const isFECrew = (name: string): name is FECrewName => {
return name.slice(0, 2) === 'FE';
};
const isBNCrew = (name: string): name is BNCrewName => {
return name.slice(0, 2) === 'BN';
};
const isANCrew = (name: string): name is ANCrewName => {
return name.slice(0, 2) === 'AN';
};
νλ³ ν¨μλ₯Ό λ°νμΌλ‘ κ³Όμ λ³ μλ‘ λ€λ₯Έ μ΄λͺ¨μ§λ₯Ό λΆμ΄λ ν¨μλ λ€μκ³Ό κ°μ΅λλ€.
const addFNEmoji = (name: FECrewName) => name + 'πΊ';
const addBNEmoji = (name: BNCrewName) => name + 'π½';
const addANEmoji = (name: ANCrewName) => name + 'π±';
μ΄λ²μ μ°ν μ½ κ΅¬μ±μλ€μ ν¬ν¨ν λ°°μ΄(μΈλΆμμ λ€μ΄μ€λ λ°μ΄ν°λΌκ³ μκ°νλ©΄ λ©λλ€.)μ 맀κ°λ³μλ‘ λ°κ³ μν κ³Όμ μ λ°λΌ μ΄λͺ¨μ§λ₯Ό λΆμ΄λ ν¨μμ λλ€.
const addEmoji = (people: string[]) => {
return people.map((name) => {
if (isFECrew(name)) {
return addFNEmoji(name); // name: FECrewName
} else if (isBNCrew(name)) {
return addBNEmoji(name); // name: BNCrewName
} else if (isANCrew(name)) {
return addANEmoji(name); // name: ANCrewName
}
return name + 'π'; // name: string
});
};
const feCrews = addEmoji([
'FE_λ
Έμ',
'ν¬λ‘ ',
'FE_ν¬λΉ',
'BN_μΏ λ§',
'μ½λ',
'FE_νλ―Έ',
'FE_μ λ‘',
'AN_루루',
]);
// [
// 'FE_λ
ΈμπΊ',
// 'ν¬λ‘ π',
// 'FE_ν¬λΉπΊ',
// 'BN_μΏ λ§π½',
// 'μ½λπ',
// 'FE_νλ―ΈπΊ',
// 'FE_μ λ‘πΊ',
// 'AN_루루π±',
// ];
2-2. κΆκΈμ
μ± μ μμ λ κ·Έλ κ³ μμ μμ λ κ·Έλ κ³ ... κ΅³μ΄ νμ μ΄ νμνκ°...? μ λ§ νμ μμ€ν μ μν΄μ μν κΈ°λ²μ μ¬μ©νλ€λ©΄ μ»λ μ΄μ μ λ¨μ§ λ¨μν μ€μλ₯Ό λ°©μ§νλ κ²μΈμ§ κΆκΈνλ€. μλ₯Ό λ€μ΄ ν¨μ νΈμΆμ μλͺ»νλ€κ±°λ νλ..!
μ»μ μ μλ μ΄μ μ λ μμλ³΄κ³ μΆλ€.
3. μ λ ¬λμ΄ μλ?
μν κΈ°λ²μ νμ μμ€ν λ΄μμ ννν μ μλ μλ§μ μμ±λ€μ λͺ¨λΈλ§νλ λ° μ¬μ©λλ€. μ¬κΈ°μ λ§νλ μμ±μ΄λ κ·Έ νμ μ νΉμ§μ΄λΌκ³ μκ°νλ©΄ λλ€. μλ₯Ό λ€μ΄ 'λ°°μ΄μ΄ μ λ ¬λμ΄ μλ€.'λΌλ μμ±(νΉμ§)μ λͺ¨λΈλ§νκΈ° μν΄μ λ€μκ³Ό κ°μ΄ μ¬μ©ν μ μλ€.
type SortedList<T> = T[] & { _brand: 'sorted' };
λ°°μ΄μ΄λ©΄μ _brand ν€λ₯Ό κ°μ§ μ μλ€. μ΄λ¬ν νμ λν νμ μμ€ν μ μμμ΄λΌκ³ ν μ μλ€.
3-1. μ΄μ§ κ²μ νκΈ°
μ΄μ§ κ²μμ μ΄λ―Έ μ λ ¬λ μνμ λ°°μ΄μ κ°μ§κ³ κ²μμ μ€μνλ€. μ¦, μ λ ¬μ΄ λμ΄ μμ§ μλ€λ©΄ μ΄μ§ κ²μ μμ²΄κ° μ±λ¦½μ΄ λμ§ μλλ€. λλ¬Έμ μμμ λ§λ SortedList<T> νμ μ νμ©ν΄μΌ μμ μ±μ ν보ν μ μλ€.
λ€μμ μ΄λ€ λ°°μ΄μ΄ SortedList<T> νμ μΈμ§ νλ³νλ ν¨μμ μ΄μ§ κ²μμ νλ ν¨μμ΄λ€.
const isSorted = <T>(list: T[]): list is SortedList<T> => {
for (let i = 1; i < list.length; i++) {
if (list[i] < list[i - 1]) return false;
}
return true;
};
const binarySearch = <T>(list: SortedList<T>, item: T): boolean => {
let low = 0;
let high = list.length - 1;
while (high >= low) {
const mid = low + Math.floor((high - low) / 2);
const v = list[mid];
if (item === v) return true;
[low, high] = item > v ? [mid + 1, high] : [low, mid - 1];
}
return false;
};
binarySearch ν¨μλ SortedList<T> νμ μ 맀κ°λ³μλ‘ λ°λλ€. λλ¬Έμ΄ isSorted ν¨μλ₯Ό ν΅κ³Όν λ°°μ΄λ§μ΄ μ΄μ§ κ²μμ ν μ μλ€. μ€μ μ¬μ©μ λ€μκ³Ό κ°λ€.
const isContainItemInBinary = <T>(list: T[], item: T) => {
if (!isSorted(list)) return false; // list: T[]
return binarySearch(list, item); // list: SortedList<T>
};
console.log(isContainItemInBinary([1, 2, 3, 4, 5], 3)); // true
console.log(isContainItemInBinary([1, 2, 3, 4, 5], 8)); // false
console.log(isContainItemInBinary([1, 2, 3, 4, 6, 5], 3)); // false
4. number νμ μ μν λΆμ΄κΈ°
λ§μ§λ§μΌλ‘ number νμ μ μνλ₯Ό λΆμ¬λ³΄λλ‘ νκ² μ΅λλ€. κ°μ μ«μμ¬λ λ¨μμ λ°λΌ κ·Έ μ«μμ ν¬κΈ°κ° λ¬λΌμ§ μ μμ΅λλ€.
type Meters = number & { _brand: 'meters' };
type Kilometers = number & { _brand: 'kilometers' };
const meters = (number: number) => number as Meters;
const kilometers = (number: number) => number as Kilometers;
const distance1 = meters(100); // distance1: Meters
const distance2 = kilometers(100); // distance2: Kilometers
distance1, distance2 λͺ¨λ κ°μ 100μ λλ€. νμ§λ§ νμ μ΄ λ€λ¦ λλ€. μ΄λ κ² μ¬λ¬ λ¨μκ° μ¬μ©λλ κ²½μ° μ«μμ λ¨μμ λ°λΌ λ€λ₯Έ μνλ₯Ό λΆμ¬ λ¬Έμνν μ μμ΅λλ€.
νμ§λ§ μ°μ μ°μ°μ νκ² λλ€λ©΄ μνκ° μμ΄μ§ number νμ μΌλ‘ λ°λκ² λ©λλ€. λλ¬Έμ μ€μ λ‘ μ¬μ©νκΈ°μλ λ¬΄λ¦¬κ° μμ΅λλ€.