๋ณธ๋ฌธ์œผ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

"TypeScript" ํƒœ๊ทธ๋กœ ์—ฐ๊ฒฐ๋œ 3๊ฐœ ๊ฒŒ์‹œ๋ฌผ๊ฐœ์˜ ๊ฒŒ์‹œ๋ฌผ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

๋ชจ๋“  ํƒœ๊ทธ ๋ณด๊ธฐ

ยท ์•ฝ 3๋ถ„

React + TypeScript + Storybook + ESLint ์„ธํŒ…#

ํ”„๋กœ์ ํŠธ ์ƒ์„ฑ#

yarn create react-app my-app-name --template typescript

CRA + ESLint + Prettier#

Create-React-App with TypeScript, ESLint, Prettier, and Github Actions

Husky + lint-staged#

์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํŒŒ์ผ ์ง€์šฐ๊ธฐ#

.gitignore#

.gitignore ๋Š” ํ”„๋กœ์ ํŠธ์—์„œ ์ค‘์š”ํ•œ ๊ฒƒ์œผ๋กœ ์‘์šฉ ํ”„๋กœ๊ทธ๋žจ ์ž์ฒด์—์„œ ์ƒ์„ฑํ•œ ํŒŒ์ผ์ด๋‚˜ ๋””๋ ‰ํ† ๋ฆฌ๋ฅผ ๋‚˜์—ดํ•ด์•ผ ํ•œ๋‹ค. ๊ฐ€์žฅ ์ข‹์€ ์˜ˆ๋Š” ์บ์‹œ ํŒŒ์ผ, ๋กœ๊ทธ, ๋กœ์ปฌ ๊ตฌ์„ฑ ๋“ฑ์ด๋‹ค.

.vscode , .idea , .DS_Store ๋“ฑ์€ ํ”„๋กœ์ ํŠธ์˜ .gitignore ๋ณด๋‹ค ์ปดํ“จํ„ฐ์˜ ๊ธ€๋กœ๋ฒŒ .gitignore ์— ์ถ”๊ฐ€ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๋‹ค.

๊ธ€๋กœ๋ฒŒ .gitignore ๋Š” home ๋””๋ ‰ํ† ๋ฆฌ์˜ .gitconfig ์„ ๋ณด๋ฉด ์•Œ ์ˆ˜ ์žˆ๋‹ค.

[core]    excludesfile = /Users/{home}/.gitignore_global

Don't put .idea and .vscode directories to project's .gitignore

ํด๋” ๊ตฌ์„ฑํ•˜๊ธฐ#

โ””โ”€โ”€ src    โ”œโ”€โ”€ components        โ””โ”€โ”€ src    โ”œโ”€โ”€ pages    โ”œโ”€โ”€ App.tsx    โ””โ”€โ”€ index.tsx

Storybook ์„ค์น˜ํ•˜๊ธฐ#

npx sb init

https://storybook.js.org/docs/react/get-started/install


์›น ํฐํŠธ ์‚ฌ์šฉ#

NAVER D2


free illustration#

unDraw - Open source illustrations for any idea


Styled Components#

The Advanced Way to Style with Styled Components

Thoughts around design systems: implementationโ€Š-โ€ŠReact, Styled-Components, and more

How does the ampersand work in styled-components?

The Sass Ampersand | CSS-Tricks

Type Guard#

https://www.typescriptlang.org/docs/handbook/advanced-types.html

validate css unit value#

https://www.reddit.com/r/learnjavascript/comments/8u5pew/function_that_can_validate_if_a_string_is_a_valid/

ยท ์•ฝ 2๋ถ„

ts-node ์ ˆ๋Œ€๊ฒฝ๋กœ#

ts-node ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์„ค์น˜

yarn add -D typescript @types/node nodemon ts-node tsconfig-paths

package.json์—์„œ nodemon์œผ๋กœ ์‹คํ–‰

"scripts": {  "dev": "nodemon -r ts-node src/index.ts" // nodemon src/index.ts๋กœ๋„ ์‹คํ–‰๋จ}

tsconfig.json์— baseUrl๊ณผ path ์„ค์ •

"baseUrl": ".","paths": {  "src/*": ["src/*"]}

โ†’ ์ด์ œ ์ปดํŒŒ์ผ์€ ๋˜์ง€๋งŒ, ์ปดํŒŒ์ผ๋œ ํŒŒ์ผ์„ node, ts-node๊ฐ€ ์ธ์‹ํ•˜์ง€ ๋ชปํ•œ๋‹ค.

โ†’ tsconfig-paths ๋ชจ๋“ˆ๋กœ ๊ฐ€๋Šฅ

"scripts": {  "dev": "nodemon --exec ts-node -r tsconfig-paths/register src/index.ts"}

๋นŒ๋“œ ํ›„์—๋„ ๋™์ž‘ํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด

  1. ts-node ๋ชจ๋“ˆ์„ ํ†ตํ•ด ์ž‘๋™์‹œํ‚ค๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

    "scripts": {    "start": "ts-node -r tsconfig-paths/register dist/index.js"    // ๋˜๋Š”    // "start": "node -r ts-node/register/transpile-only -r tsconfig-paths/register dist/index.js"}
  2. tsconfig-paths์˜ bootstrap์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•.

    // tsconfig-paths-bootstrap.jsconst tsConfigPathFile = require('./tsconfig.paths.json');const tsConfig = require('./tsconfig.json');const tsConfigPaths = require('tsconfig-paths');
    tsConfigPaths.register({    baseUrl: tsConfig.compilerOptions.outDir,    paths: tsConfigPathFile.compilerOptions.paths,});
    // package.json"scripts" {  "start": "node -r ./tsconfig-paths-bootstrap.js dist/src/index.js"}

์ฐธ๊ณ ์ž๋ฃŒ#

ํƒ€์ž…์Šคํฌ๋ฆฝํŠธ์—์„œ ์ ˆ๋Œ€๊ฒฝ๋กœ ์“ฐ๊ธฐ

typescript + node absolute path

ยท ์•ฝ 14๋ถ„

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 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`
    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 ๊นŒ์ง€ ์‹œ์ฒญ ...