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); // NaNstrictNullChecks ์ต์
์ ์ผ๋ฉด ๋ชจ๋ ํ์
์ ์๋์ผ๋ก ํฌํจ๋์ด ์๋ 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 ์ฒ๋ผ ์ฐ๋ ๊ผผ์ ? - ์์ง ์ ์ดํด๋ฅผ ๋ชปํจ
function
array
intersection
union
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 Guard#
function 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`
`Item T` - T๊ฐ string์ด๋ฉด StringContainer, ์๋๋ฉด NumberContainer
`Item T` - T๊ฐ string์ด๋ฉด StringContainer, number๋ฉด NumberContainer, ์๋๋ฉด ์ฌ์ฉ ๋ถ๊ฐ
`ArrayFilter T`
1:11:12 ๊น์ง ์์ฒญ ...