#
TypeScript#
๋ชฉํTypeScript๋ก ํ์ดํ์ ์ํ๋ฉด, ๋ฐํ์ ์ ์ ๋ฏธ๋ฆฌ ์ค๋ฅ๋ฅผ ์ ์ ์์ต๋๋ค.
์ฝ๋์ ๊ตฌํ์๊ฐ ์ฌ์ฉ์์๊ฒ ์๋๋ฅผ ์ ๋ฌํ ์ ์๋ค.
#
ํ์ ์์คํ ์ปดํ์ผ๋ฌ์๊ฒ ์ฌ์ฉํ๋ ํ์ ์ ๋ช ์์ ์ผ๋ก ์ง์ ํ๋ ์์คํ
์ปดํ์ผ๋ฌ๊ฐ ์๋์ผ๋ก ํ์ ์ ์ถ๋ก ํ๋ ์์คํ
โ ํ์ ์คํฌ๋ฆฝํธ๋ ํ์ ์ ๋ช ์์ ์ผ๋ก ์ง์ ํ ์ ์๊ณ ๋ช ์์ ์ผ๋ก ์ง์ ํ์ง ์์ผ๋ฉด์ปดํ์ผ๋ฌ๊ฐ ์๋์ผ๋ก ํ์ ์ ์ถ๋ก ํ๋ค.
#
๋ช ์์ ํ์ vs ์ถ๋ก// ํจ์์ ๋ฆฌํด ํ์
์ number๋ก ์ถ๋ก ๋๋ค.// ํ์ง๋ง ๋งค๊ฐ๋ณ์์ ํ์
์ ๋ช
์์ ์ผ๋ก ์ง์ ํ์ง ์์์, any๋ก ์ถ๋ก ๋๋ค.function f(a) { return a * 38;}
// ์ฌ์ฉ์๋ a๊ฐ any ์ด๊ธฐ ๋๋ฌธ์ ์ฌ์ฉ๋ฒ์ ๋ง๊ฒ ํจ์๋ฅผ ์คํํ์ง๋ง, ์ฌ์ฉ์๋ ์์ธกํ์ง ๋ชปํ ๊ฒฐ๊ณผ๋ฅผ ์ป๊ฒ ๋๋ค.console.log(f(10)); // 380console.log(f('Mark')); // NaN
์ด๊ฒ์ noImplicitAny
์ต์
์ ํตํด ๋ฐฉ์ดํ ์ ์๋ค. ํ์
์ ๋ช
์์ ์ผ๋ก ์ง์ ํ์ง ์์ ๊ฒฝ์ฐ any
๋ก ์ถ๋ก ๋๋ ๋ณ์๊ฐ ์์ผ๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
// ๋งค๊ฐ๋ณ์์ ํ์
์ ๋ช
์์ ์ผ๋ก ์ง์ ํ๋ค.// ๋ช
์์ ์ผ๋ก ์ง์ ํ์ง ์์ ๋ฆฌํด ํ์
์ number๋ก ์ถ๋ก ๋๋ค.function f(a: number) { if (a > 0) { return a * 38; }}
// ์ฌ์ฉ์๋ ์ฌ์ฉ๋ฒ์ ๋ง๊ฒ ์ซ์ํ์ ์ฌ์ฉํด์ ํจ์๋ฅผ ์คํํ์ง๋ง,// ์ค์ ๋ก๋ ํจ์์ ๋ฆฌํด์ด undefined๋ก, undefined + 5๊ฐ ์คํ๋์ด NaN์ด ์ถ๋ ฅ๋๋ค.
console.log(f(5)); // 190;console.log(f(-5) + 5); // NaN
strictNullChecks
์ต์
์ ์ผ๋ฉด ๋ชจ๋ ํ์
์ ์๋์ผ๋ก ํฌํจ๋์ด ์๋ null
๊ณผ undefined
๋ฅผ ์ ๊ฑฐํด์ค๋ค.
// ์ด ํจ์์ ๋ฆฌํด ํ์
์ number | undefined๋ก ์ถ๋ก ๋๋ค.function f(a: number) { if (a > 0) { return a * 38; }}
// ํด๋น ํจ์์ ๋ฆฌํด ํ์
์ number | undefined ์ด๊ธฐ ๋๋ฌธ์,// ํ์
์ ๋ฐ๋ฅด๋ฉด ์ปดํ์ผ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.// ์ปดํ์ผ ์๋ฌ๋ฅผ ๊ณ ์น๊ธฐ ์ํด ์ฌ์ฉ์์ ์์ฑ์๊ฐ ์๋
ผํด์ผ ํ๋ค.console.log(f(-5) + 5); // error TS2532: Object is possibly 'undefined'
ํ์ง๋ง ๊ฐ๊ธ์ ์ด๋ฉด ๋ช ์์ ์ผ๋ก ๋ฆฌํด ํ์ ์ ์ง์ ํ๋ ๊ฒ์ด ์ข๋ค.
noImplicitReturns
์ต์
์ ์ผ๋ฉด ํจ์ ๋ด์ ๋ชจ๋ ์ฝ๋์ ์ค๊ธฐ๊ฐ ๊ฐ์ ๋ฆฌํดํ์ง ์์ผ๋ฉด ์ปดํ์ผ ์๋ฌ๋ฅผ ๋ฐ์์ํจ๋ค.
// error TS7030: Not all code paths return a valuefunction f(a: number) { if (a > 0) { return a * 38; }}
์ด๋ก ์ธํด ๋ฆฌํด ํ์
์ ๋ช
์ํ๊ณ , ๋ชจ๋ ์ค๊ธฐ์ return
์ ์ง์ ํ๋๋ก ๊ตฌํ์์๊ฒ๊ฐ์ ํ ์ ์๋ค.
#
๋งค๊ฐ๋ณ์์ object๊ฐ ๋ค์ด์ค๋ ๊ฒฝ์ฐfunction f(a) { return `์ด๋ฆ์ ${a.name}์ด๊ณ ์ฐ๋ น๋๋ ${Math.floor(a.age / 10) * 10)}๋ ์
๋๋ค.`}
console.log(f({ name: 'Mark', age: 38 })); // ์ด๋ฆ์ Mark์ด๊ณ , ์ฐ๋ น๋๋ 30๋ ์
๋๋ค.console.log(f('Mark')); // ์ด๋ฆ์ undefined์ด๊ณ , ์ฐ๋ น๋๋ NaN๋ ์
๋๋ค.
JavaScript์์๋ ์ ์ฝ์ฌํญ์ผ๋ก ์๋ ค์ฃผ์ง ์๊ธฐ ๋๋ฌธ์ ๋ฐํ์์์ ์ค๋ฅ๋ฅผ ํ์ ํ ์์๋ค.
- Object literal type
function f(a: { name: string, age: number }): string { return `์ด๋ฆ์ ${a.name}์ด๊ณ ์ฐ๋ น๋๋ ${Math.floor(a.age / 10) * 10)}๋ ์
๋๋ค.`}
- ๋๋ง์ ํ์ ์ ๋ง๋๋ ๋ฐฉ๋ฒ
interface PersonInterface { name: string; age: number;}
type PersonTypeAlias = { name: string; age: number;}
function f1(a: PersonInterface): string { return `์ด๋ฆ์ ${a.name}์ด๊ณ ์ฐ๋ น๋๋ ${Math.floor(a.age / 10) * 10)}๋ ์
๋๋ค.`}
function f2(a: PersonTypeAlias): string { return `์ด๋ฆ์ ${a.name}์ด๊ณ ์ฐ๋ น๋๋ ${Math.floor(a.age / 10) * 10)}๋ ์
๋๋ค.`}
#
interface vs type alias#
structural vs nominal type systemํ์ ์คํฌ๋ฆฝํธ๋ structural ํ์ ์์คํ ์ ์ฌ์ฉํ๋ค.
structural type system : ๊ตฌ์กฐ๊ฐ ๊ฐ์ผ๋ฉด, ๊ฐ์ ํ์ ์ด๋ค.
nominal type system : ๊ตฌ์กฐ๊ฐ ๊ฐ์๋ ์ด๋ฆ์ด ๋ค๋ฅด๋ฉด, ๋ค๋ฅธ ํ์ ์ด๋ค.
interface IPerson { name: string; age: number; speak(): string;}
type PersonType = { name: string; age: number; speak(): string;};
typescript๋ฅผ nominal ์ฒ๋ผ ์ฐ๋ ๊ผผ์ ? - ์์ง ์ ์ดํด๋ฅผ ๋ชปํจ
type PersonID = string & {readonly brand: unique symbol}; function PersonID(id: string): PersonID { return id as PersonID;} function getPersonById(id: PersonID) {} getPersonById(PersonID('id-aaaaaa'));getPersonById('id-aaaaaa');
function
type EatType = (food: string) => void; interface IEat { (food: string): void;}
array
type PersonList = string[]; interface IPersonList { [index: number]: string;}
intersection
interface ErrorHandling { success: boolean; error?: {message: string};} interface ArtistsData { artists: {name: string}[];} type ArtistsResponseType = ArtistsData & ErrorHandling; interface IArtistsResponse extends ArtistsData, ErrorHandling {}
union
interface Bird { fly(): void; layEggs(): void;} interface Fish { swim(): void; layEggs(): void;} type BirdType = { fly(): void; layEggs(): void;}; type FishType = { swim(): void; layEggs(): void;}; // union์ type alias๋ฅผ ์ฌ์ฉํด์ผ ํ๋ค.type PetType = Bird | Fish; // ์๋ฌ ๋ฐ์interface IPet extends PetType {}class Pet implements PetType {} // ๊ฐ๋ฅinterface IBird extends BirdType {}class IFish implements FishType {}
#
Declaration Merging - interface์ค๋ณต๋๋ ์ด๋ฆ์ผ๋ก ์ ์ธ๋์์ ๋ ๋จธ์ง๋๋ ๊ธฐ๋ฅ์ interface์์๋ง ์ ๊ณต๋๋ค.
type์ ์ค๋ณต์์ ์๋ฌ๊ฐ ๋ฐ์ํ๋ค.
์ด๋ ์ธ๋ถ ์ ํธ์์ ์ฌ์ฉ์๊ฐ ์ํ๋ ํ์ ์ ์ถ๊ฐํด์ ์ฌ์ฉํ๋ ๊ฒ์ด ๊ฐ๋ฅํ๋ค.
interface MergingInterface { a: string;}
interface MergingInterface { b: string;}
let mi: MergingInterface;// mi: { a: string, b: string }
์ธ์ type์ ์ฌ์ฉํ๊ณ ์ธ์ interface๋ฅผ ์ฌ์ฉํ๋๊ฐ
์๋ฏธ์ ์ผ๋ก, ์ญํ ์ ์ผ๋ก type alias๋ฅผ ํ์ ์ ๋ณ์นญ์ ๋ถ์ผ ๋ ์ฌ์ฉํ๋ค.
์ฆ, ์ด๋ฏธ ์๋ ํ์ ์ ๋ณ์นญ์ ๋ถ์ฌ ๋ค๋ฅธ ์ด๋ฆ์ผ๋ก ๋ถ๋ฅด๊ณ ์ถ์ ๋, ์ด๋ฏธ ์๋ ํ์ ์ union์ผ๋ก ์กฐํฉํ ๋ ์ฌ์ฉํ๋ค.
๊ทธ ์ธ์ ์๋ก์ด ํ์ ์ ์์ฑํ ๋ interface๋ฅผ ์ฌ์ฉํ๋ค.
- ์ค์ ๋์ ์์ ์ฐจ์ด๋ Merging์์์ ์ฐจ์ด๊ฐ ๊ฐ์ฅ ์ค์ํ๋ค.
#
์๋ธ ํ์ ๊ณผ ์ํผ ํ์์งํฉ์ ๊ด๊ณ์์ ํฌํจ๋๋ ์ชฝ์ด ์๋ธ ํ์ , ํฌํจํ๋ ์ชฝ์ด ์ํผ ํ์ ์ด๋ค.
let sub1: 1 = 1;let sup1: number = sub1;sub1 = sup1; // error! Type 'number' is not assignable to type '1'.
let sub2: number[] = [1];let sup2: object = sub2;sub2 = sup2; // error! Type '{}' is missing the following the properties from type 'number[]': length, pop, push, concat, and 16 more.
let sub3: [number, number] = [1, 2];let sup3: number[] = sub3;sub3 = sup3; // error! Type 'number[]' is not assignable to type '[number, number]'. Target requires 2 element(s) but source may have fewer.
let sub4: number = 1;let sup4: any = sub4;sub4 = sup4;
let sub5: never = 0 as never;let sup5: number = sub5;sub5 = sup5; // Type 'number' is not assignable to type 'never'.
class Animal {}class Dog extends Animal { eat() {}}
let sub6: Dog = new Dog();let sup6: Animal = sub6;sub6 = sup6; // Property 'eat' is missing in type 'Animal' but required in type 'Dog'.
โ
any
์never
๋ ์ถํ์ ๋ค์ ํ์ต
#
๊ฐ๊ฑฐ๋ ์๋ธ ํ์ ์ธ ๊ฒฝ์ฐ ํ ๋น์ด ๊ฐ๋ฅํ๋ค. โ ๊ณต๋ณlet sub7: string = '';let sup7: string | number = sub7;
// object - ๊ฐ๊ฐ์ ํ๋กํผํฐ๊ฐ ๋์ํ๋ ํ๋กํผํฐ์ ๊ฐ๊ฑฐ๋ ์๋ธํ์
์ด์ด์ผ ํ๋ค.let sub8: {a: string; b: number} = {a: '', b: 1};let sup8: {a: string | number; b: number} = sub8;
// array - object์ ๋ง์ฐฌ๊ฐ์งlet sub9: Array<{a: string; b: number}> = [{a: '', b: 1}];let sup9: Array<{a: string | number; b: number}> = sub9;
#
ํจ์์ ๋งค๊ฐ๋ณ์ ํ์ ๋ง ๊ฐ๊ฑฐ๋ ์ํผํ์ ์ธ ๊ฒฝ์ฐ, ํ ๋น์ด ๊ฐ๋ฅํ๋ค โ ๋ฐ๋ณclass Person {}class Developer extends Person { coding() {}}class StartupDeveloper extends Developer { burning() {}}
function tellme(f: (d: Developer) => Developer) {}
// Developer => Developer ์๋ค๊ฐ Developer => Developer ๋ฅผ ํ ๋นํ๋ ๊ฒฝ์ฐtellme(function dToD(d: Developer): Developer { return new Developer();});
// Developer => Developer ์๋ค๊ฐ Person => Developer ๋ฅผ ํ ๋นํ๋ ๊ฒฝ์ฐ// ๋ฐ๋ณtellme(function pToD(d: Person): Developer { return new Developer();});
// Developer => Developer ์๋ค๊ฐ StartupDeveloper => Developer ๋ฅผ ํ ๋นํ๋ ๊ฒฝ์ฐ// strictFunctionType์ผ๋ก ์๋ฌ ํ์ ๊ฐ๋ฅtellme(function sToD(d: StartupDeveloper): Developer { return new Developer();});
#
any์ ๋ ฅ์ ๋ง์๋๋ก
ํจ์ ๊ตฌํ์ด ์์ ๋กญ๊ฒ โ ์์ ๊ฐ ํญ์ ์ข์ ๊ฑด ์๋๋ค.
#
unknown์ ๋ ฅ์ ๋ง์๋๋ก
ํจ์ ๊ตฌํ์ ๋ฌธ์ ์๋๋ก
#
ํ์ ์ถ๋ก ์ดํดํ๊ธฐlet๊ณผ const์ ํ์ ์ถ๋ก (+ as const)
as const
let a = 'Mark'; // stringconst b = 'Mark'; // 'Mark' => literal type
let c = 38; // numberconst d = 38; // 38 => literal type
let e = false; // booleanconst f = false; // false => literal type
let g = ['Mark', 'Haeun']; // string[]const h = ['Mark', 'Haeun']; // string[]
const i = ['Mark', 'Haeun', 'Bokdang'] as const; // readonly ['Mark', 'Haeun', 'Bokdang']
- best common type (๊ฐ์ฅ ๊ณตํต์ ์ธ ํ์ ์ ์ถ๋ก ํด๋ธ๋ค)
let j = [0, 1, null]; // (number | null)[]const k = [0, 1, null]; // (number | null)[]
class Animal {}class Rhino extends Animal {}class Elephant extends Animal {}class Snake extends Animal {}
let l = [new Rhino(), new Elephant(), new Snake()]; // (Rhino | Elephant | Snake)[]const m = [new Rhino(), new Elephant(), new Snake()]; // (Rhino | Elephant | Snake)[]const n = [new Animal(), new Rhino(), new Elephant(), new Snake()]; // Animal[]const o: Animal[] = [new Rhino(), new Elephant(), new Snake()]; // Animal[]
- Contextual Typing - ์์น์ ๋ฐ๋ผ ์ถ๋ก ์ด ๋ค๋ฆ
// Parameter 'e' implicitly has an 'any' type.const click = (e) => { e; // any};
document.addEventListener('click', click);document.addEventListener('click', (e) => { e; // MouseEvent});
#
Type Guard๋ก ์์ ํจ์ ํ์ ํ๊ธฐ#
typeof Type Guard - ๋ณดํต Primitive ํ์ ์ผ ๊ฒฝ์ฐ- typeof๋ก primitive ํ์ ์ ๊ฑธ๋ฌ๋ผ ์ ์๋ค.
function getNumber(value: number | string): number { value; // number | string if (typeof value == 'number') { return value; } value; // string return -1;}
#
instanceof Type Guard - Error ๊ฐ์ฒด ๊ตฌ๋ถ์ ๋ง์ด ์ฐ์ธ๋ค.class NegativeNumberError extends Error {}
function getNumber(value: number): number | NegativeNumberError { if (value < 0) return new NegativeNumberError();
return value;}
function main() { const num = getNumber(-10);
if (num instanceof NegativeNumberError) { return; }
num; // number}
#
in operator Type Guard - object์ ํ๋กํผํฐ ์ ๋ฌด๋ก ์ฒ๋ฆฌํ๋ ๊ฒฝ์ฐinterface Admin { id: string; role: string;}
interface User { id: string; emain: string;}
function redirect(user: Admin | User) { if ('role' in user) { routeToAdminPage(user.role); } else { routeToHomePage(user.email); }}
#
literal Type Guard - object์ ํ๋กํผํฐ๊ฐ ๊ฐ๊ณ , ํ์ ์ด ๋ค๋ฅธ ๊ฒฝ์ฐinterface IMachine { type: string;}
class Car implements IMachine { type: 'CAR'; wheel: number;}
class Boat implements IMachine { type: 'BOAT'; motor: number;}
function getWheelOrMotor(machine: Car | Boat): number { if (machine.type === 'CAR') { return machine.wheel; } else { return machine.motor; }}
#
custom Type Guardfunction getWheelOrMoter(machine: any): number { if (isCar(machine)) { return machine.wheel; } else if (isBoat(machine)) { return machine.motor; } else { return -1; }}
function isCar(arg: any): arg is Car { return arg.type === 'CAR';}
function isBoat(arg: any): arg is Boat { return arg.type === 'BOAT';}
#
Conditional Type`Item T`
interface StringContainer { value: string; format(): string; split(): string[];} interface NumberContainer { value: number; nearestPrime: number; round(): number;} type Item1<T> = { id: T; container: any;}; const item1: Item1<string> = { id: 'aaaaaa', container: null, // container type์ any๋ฅผ ์ฌ์ฉํ๊ธฐ ๋๋ฌธ์ null์ ๋ฃ๋ ๊ฒ๋ ๊ฐ๋ฅํ๋ค.};
`Item T` - T๊ฐ string์ด๋ฉด StringContainer, ์๋๋ฉด NumberContainer
type Item2<T> = { id: T; container: T extends string ? StringContainer : NumberContainer;}; const item2: Item2<string> = { id: 'aaaaaa', container: null, // Type 'null' is not assignable to type 'StringContainer'.};
`Item T` - T๊ฐ string์ด๋ฉด StringContainer, number๋ฉด NumberContainer, ์๋๋ฉด ์ฌ์ฉ ๋ถ๊ฐ
type Item3<T> = { id: T extends string | number ? T : never; container: T extends string ? StringContainer : T extends number ? NumberContainer : never;}; const item3: Item3<boolean> = { id: true, // Type 'boolean' is not assignable to type 'never'. container: null, // Type 'null' is not assignable to type 'never'.};
`ArrayFilter T`
type ArrayFilter<T> = T extends any[] ? T : never;type StringsOrNumbers = ArrayFilter<string | number | string[] | number[]>;
1:11:12 ๊น์ง ์์ฒญ ...