TypeScript 기초 강좌

2woongjae@gmail.com

Chapter 1. TypeScript Overview

TypeScript = Language

  • 타입스크립트는 ' 프로그래밍 언어 ' 입니다.
  • 타입스크립트는 ' Compiled Language ' 입니다.
    • 전통적인 Compiled Language 와는 다른 점이 많습니다.
    • 그래서 ' Transpile ' 이라는 용어를 사용하기도 합니다.
  • 자바스크립트는 ' Interpreted Language ' 입니다.

Compiled

  • 컴파일이 필요 O
  • 컴파일러가 필요 O
  • 컴파일하는 시점 O
    • => 컴파일 타임
  • 컴파일된 결과물을 실행
  • 컴파일된 결과물을 실행하는 시점

Interpreted

  • 컴파일이 필요 X 
  • 컴파일러가 필요 X
  • 컴파일하는 시점 X
  • 코드 자체를 실행
  • 코드를 실행하는 시점 o
    • = 런타임

정적 타입 언어 VS 동적 타입 언어

Traditional Compiled Language

  • 컴파일 언어라고 한다.
  • C, C++, Go, C#, Java, ...
  • 프로그래머가 작성한 ' Source Code ' 를 기계어로 변환하는 과정을  ' Compile ' 이라고 한다.
  • 기계어로 변환된 결과물을 ' Object Code ' (목적 코드)라 한다.
  • ' Compile ' 하는 프로그램을 ' Compiler ' 라고 한다.
  • ' Compile ' 하는 동안을 ' Compile Time ' 이라고 한다.
  • 컴파일된 코드는 프로세서에 따라 다르다.
  • 소스 코드에서는 OS 에 따라 라이브러리가 다르다.
  • 컴파일된 코드는 작은 크기에 최적화된다.
  • 일반적으로 실행시 기계어로 바꾸는 방식(인터프리터 언어)보다 빠르다.
    • 실행시 기계어로 바꿔주는 연산이 필요없기 때문이다.

Traditional Compiled Language

  • 컴파일된 코드들은 ' Linking ' 이라는 과정을 통해 실행 파일로 만들어 진다.
    • 컴파일된 여러 목적 코드들을 합치고 라이브러리를 추가한다.
    • ' Linking ' 하는 프로그램을 ' Linker ' 라고 한다.
    • 컴파일이라는 말을 링킹까지 포함하여 말하기도 한다.
  • 컴파일된 코드는 프로세서에 의존적이다.
  • 컴파일된 코드는 작은 크기에 최적화된다.
  • 일반적으로 실행시 기계어로 바꾸는 방식(인터프리터 언어)보다 빠르다.
    • 실행시 기계어로 바꿔주는 연산이 필요없기 때문이다.

컴파일러

Chapter 2. 개발 환경 구축 및 컴파일러 사용

C. 에디터

A. 실행 환경

B. 컴파일러

A. 자바스크립트 실행 환경 설치

node.js

Chrome's

V8 JavaScript Engine

을 사용하여,

자바스크립트를 해석하고

OS 레벨에서의 API를 제공하는

서버사이드 용

자바스크립트 런타임 환경

browser

HTML 을 동적으로 만들기 위해

브라우저에서

자바스크립트를 해석하고,

DOM 을 제어할 수 있도록 하는

자바스크립트 런타임 환경

node.js 설치

browser 설치

  • 있는거 쓰시면 됩니다.
  • 그래도 크롬이 좀... 낫죠 ?

B. 타입스크립트 컴파일러 설치

npm / Visual Studio plugin

  • npm
    • npm i typescript -g
    • node_modules/.bin/tsc
    • tsc source.ts
  • Visual Studio plugin 설치
    • Visual Studio 2017 / 2015 Update 3 에서는 디폴트로 설치되어 있음
    • 아니면 설치

C. 에디터 설치

IDE : 통합 개발 환경

  • Integrated Development Environment
    • 코딩, 디버그, 컴파일, 배포 등 프로그램 개발에 관련된 모든 작업을 하나의 프로그램 안에서 처리하는 환경을 제공하는 소프트웨어
    • Visual Studio
    • Visual Studio Code
    • IntelliJ IDEA / WebStorm

간단한 컴파일러 사용 예제

  • 타입스크립트 컴파일러를 글로벌로 설치 후,
    • cli 명령어로 파일 컴파일
    • 특정 프로젝트 폴더에서 타입스크립트 컴파일러 설정에 맞춰 컴파일
    • 특정 프로젝트 폴더에서 타입스크립트 컴파일러 설정에 맞춰 컴파일 (watch 모드)
  • 프로젝트에 타입스크립트 컴파일러를 설치 후,
    • .bin 안의 명령어로 파일 컴파일
    • npm 스크립트로 파일 컴파일
    • 프로젝트에 있는 타입스크립트 설정에 맞춰, npm 스크립트로 컴파일
    • 프로젝트에 있는 타입스크립트 설정에 맞춰, npm 스크립트로 컴파일 (watch 모드)

Chapter 3. IDE 활용

Visual Studio Code

Visual Studio Code

  • Compiler
    • VS Code 에 컴파일러가 내장되어 있습니다.
    • 내장된 컴파일러 버전은 VS Code 가 업데이트 되면서 올라갑니다.
      • 그래서 컴파일러 버전과 VS Code 의 버전은 상관 관계가 있습니다.
    • 내장된 컴파일러를 선택할수 있고, 직접 설치한 컴파일러를 선택할 수도 있습니다.
  • tslint

IntelliJ IDEA - TypeScript Compiler

ItelliJ IDEA - TypeScript Compiler

  • Node interpreter 를 지정해야합니다.
  • 타입스크립트 컴파일러를 지정해야합니다.
    • 내장된 타입스크립트 컴파일러도 있습니다.
    • 설치된 타입스크립트 컴파일러를 지정할 수 있습니다.
  • Use TypeScript Service
  • Enable TypeScript Compiler 를 설정해서 컴파일을 할 수 있습니다.
    • gulp 같은걸 쓰지 않아도 됩니다.

IntelliJ IDEA - TSLint

ItelliJ IDEA - TSLint

  • Enable 체크를 통해 tslint 가 자동으로 오류를 체크해줍니다.
  • Node interpreter 를 지정해야합니다.
  • TSLint 모듈을 지정해야합니다.
  • 설정 파일인 tslint.json 파일을 지정하거나, 자동으로 프로젝트 루트에서 찾도록 할 수 있습니다.

Chapter 4. Compiler Options

최상위 프로퍼티

  • compileOnSave
  • extends
  • compileOptions
  • files
  • include
  • exclude
  • typeAcquisition

compileOnSave

  • true / false (default false)
  • 최상단에 설정해야 한다 ?
  • 누가 ??
    • Visual Studio 2015 with TypeScript 1.8.4 이상
    • atom-typescript 플러그인 

extends

  • 파일 (상대) 경로명: string
  • TypeScript 2.1 New Spec
// in config/base.json
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true
  }
}


// in tsconfig.json
{
  "extends": "./configs/base",
  "files": [
    "main.ts",
    "supplemental.ts"
  ]
}

files, include, exclude

  • 셋다 설정이 없으면, 전부다 컴파일
  • files
    • 상대 혹은 절대 경로의 리스트 배열입니다.
    • exclude 보다 쎕니다.
  • include, exclude
    • glob 패턴 (마치 .gitignore)
    • include
      • exclude 보다 약합니다.
      • * 같은걸 사용하면, .ts / .tsx / .d.ts 만 include (allowJS)
    • exclude
      • 설정 안하면 4가지(node_modules, bower_components, jspm_packages, <outDir>)를 default 로 제외합니다. (아하!)
      • <outDir> 은 항상 제외합니다. (include 에 있어도)

compileOptions

{
    "type": "object",
    "description": "Instructs the TypeScript compiler how to compile .ts files.",
    "properties": {
        "charset": {
            "description": "The character set of the input files.",
            "type": "string"
        },
        "declaration": {
            "description": "Generates corresponding d.ts files.",
            "type": "boolean"
        },
        "declarationDir": {
            "type": "string",
            "description": "Specify output directory for generated declaration files. Requires TypeScript version 2.0 or later."
        },
        "diagnostics": {
            "description": "Show diagnostic information.",
            "type": "boolean"
        },
        "emitBOM": {
            "description": "Emit a UTF-8 Byte Order Mark (BOM) in the beginning of output files.",
            "type": "boolean"
        },
        "inlineSourceMap": {
            "description": "Emit a single file with source maps instead of having a separate file.",
            "type": "boolean"
        },
        "inlineSources": {
            "description": "Emit the source alongside the sourcemaps within a single file; requires --inlineSourceMap to be set.",
            "type": "boolean"
        },
        "jsx": {
            "description": "Specify JSX code generation: 'preserve', 'react', or 'react-native'.",
            "enum": [ "preserve", "react", "react-native" ]
        },
        "reactNamespace": {
            "description": "Specifies the object invoked for createElement and __spread when targeting 'react' JSX emit.",
            "type": "string"
        },
        "listFiles": {
            "description": "Print names of files part of the compilation.",
            "type": "boolean"
        },
        "locale": {
            "description": "The locale to use to show error messages, e.g. en-us.",
            "type": "string"
        },
        "mapRoot": {
            "description": "Specifies the location where debugger should locate map files instead of generated locations",
            "type": "string"
        },
        "module": {
            "description": "Specify module code generation: 'none', 'CommonJS', 'Amd', 'System', 'UMD', or 'es2015'.",
            "enum": [ "commonjs", "amd", "umd", "system", "es6", "es2015", "none" ]
        },
        "newLine": {
            "description": "Specifies the end of line sequence to be used when emitting files: 'CRLF' (dos) or 'LF' (unix).",
            "enum": [ "CRLF", "LF" ]
        },
        "noEmit": {
            "description": "Do not emit output.",
            "type": "boolean"
        },
        "noEmitHelpers": {
            "description": "Do not generate custom helper functions like __extends in compiled output.",
            "type": "boolean"
        },
        "noEmitOnError": {
            "description": "Do not emit outputs if any type checking errors were reported.",
            "type": "boolean"
        },
        "noImplicitAny": {
            "description": "Warn on expressions and declarations with an implied 'any' type.",
            "type": "boolean"
        },
        "noImplicitThis": {
            "description": "Raise error on 'this' expressions with an implied any type.",
            "type": "boolean"
        },
        "noUnusedLocals": {
            "description": "Report errors on unused locals. Requires TypeScript version 2.0 or later.",
            "type": "boolean"
        },
        "noUnusedParameters": {
            "description": "Report errors on unused parameters. Requires TypeScript version 2.0 or later.",
            "type": "boolean"
        },
        "noLib": {
            "description": "Do not include the default library file (lib.d.ts).",
            "type": "boolean"
        },
        "noResolve": {
            "description": "Do not add triple-slash references or module import targets to the list of compiled files.",
            "type": "boolean"
        },
        "skipDefaultLibCheck": {
            "type": "boolean"
        },
        "skipLibCheck": {
            "description": "Skip type checking of declaration files. Requires TypeScript version 2.0 or later.",
            "type": "boolean"
        },
        "outFile": {
            "description": "Concatenate and emit output to single file.",
            "type": "string"
        },
        "outDir": {
            "description": "Redirect output structure to the directory.",
            "type": "string"
        },
        "preserveConstEnums": {
            "description": "Do not erase const enum declarations in generated code.",
            "type": "boolean"
        },
        "pretty": {
            "description": "Stylize errors and messages using color and context (experimental).",
            "type": "boolean"
        },
        "removeComments": {
            "description": "Do not emit comments to output.",
            "type": "boolean"
        },
        "rootDir": {
            "description": "Specifies the root directory of input files. Use to control the output directory structure with --outDir.",
            "type": "string"
        },
        "isolatedModules": {
            "description": "Unconditionally emit imports for unresolved files.",
            "type": "boolean"
        },
        "sourceMap": {
            "description": "Generates corresponding '.map' file.",
            "type": "boolean"
        },
        "sourceRoot": {
            "description": "Specifies the location where debugger should locate TypeScript files instead of source locations.",
            "type": "string"
        },
        "suppressExcessPropertyErrors": {
            "description": "Suppress excess property checks for object literals.",
            "type": "boolean"
        },
        "suppressImplicitAnyIndexErrors": {
            "description": "Suppress noImplicitAny errors for indexing objects lacking index signatures.",
            "type": "boolean"
        },
        "stripInternal": {
            "description": "Do not emit declarations for code that has an '@internal' annotation.",
            "type": "boolean"
        },
        "target": {
            "description": "Specify ECMAScript target version. Permitted values are 'es3', 'es5', 'es2015', 'es2016', 'es2017' or 'esnext'.",
            "type": "string",
            "default": "es3",
            "anyOf": [
                {
                    "enum": [
                        "es3",
                        "es5",
                        "es2015",
                        "es2016",
                        "es2017",
                        "esnext"
                      ]
                }, {
                      "pattern": "^([eE][sS]([356]|(201[567])|[nN][eE][xX][tT]))$"
                }
            ]
        },
        "watch": {
            "description": "Watch input files.",
            "type": "boolean"
        },
        "experimentalDecorators": {
            "description": "Enables experimental support for ES7 decorators.",
            "type": "boolean"
        },
        "emitDecoratorMetadata": {
            "description": "Emit design-type metadata for decorated declarations in source.",
            "type": "boolean"
        },
        "moduleResolution": {
            "description": "Specifies module resolution strategy: 'node' (Node) or 'classic' (TypeScript pre 1.6) .",
            "type": "string",
            "pattern": "^(([Nn]ode)|([Cc]lassic))$",
            "default": "classic"
        },
        "allowUnusedLabels": {
            "type": "boolean",
            "description": "Do not report errors on unused labels."
        },
        "noImplicitReturns": {
            "description": "Report error when not all code paths in function return a value.",
            "type": "boolean"
        },
        "noFallthroughCasesInSwitch": {
            "description": "Report errors for fallthrough cases in switch statement.",
            "type": "boolean"
        },
        "allowUnreachableCode": {
            "description": "Do not report errors on unreachable code.",
            "type": "boolean"
        },
        "forceConsistentCasingInFileNames": {
            "description": "Disallow inconsistently-cased references to the same file.",
            "type": "boolean"
        },
        "baseUrl": {
            "description": "Base directory to resolve non-relative module names.",
            "type": "string"
        },
        "paths": {
            "description": "Specify path mapping to be computed relative to baseUrl option.",
            "type": "object"
        },
        "rootDirs": {
            "description": "Specify list of root directories to be used when resolving modules.",
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "typeRoots": {
            "description": "Specify list of directories for type definition files to be included. Requires TypeScript version 2.0 or later.",
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "types": {
            "description": "Type declaration files to be included in compilation. Requires TypeScript version 2.0 or later.",
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "traceResolution": {
            "description": "Enable tracing of the name resolution process.",
            "type": "boolean"
        },
        "allowJs": {
            "description": "Allow javascript files to be compiled.",
            "type": "boolean"
        },
        "allowSyntheticDefaultImports": {
            "description": "Allow default imports from modules with no default export. This does not affect code emit, just typechecking.",
            "type": "boolean"
        },
        "noImplicitUseStrict": {
            "description": "Do not emit 'use strict' directives in module output.",
            "type": "boolean"
        },
        "listEmittedFiles": {
            "description": "Enable to list all emitted files. Requires TypeScript version 2.0 or later.",
            "type": "boolean"
        },
        "lib": {
            "description": "Specify library file to be included in the compilation. Requires TypeScript version 2.0 or later.",
            "type": "array",
            "items": {
                "type": "string",
                "enum": [ "es5", "es6", "es2015", "es7", "es2016", "es2017", "esnext", "dom", "dom.iterable", "webworker", "scripthost", "es2015.core", "es2015.collection", "es2015.generator", "es2015.iterable", "es2015.promise", "es2015.proxy", "es2015.reflect", "es2015.symbol", "es2015.symbol.wellknown", "es2016.array.include", "es2017.object", "es2017.sharedmemory", "esnext.asynciterable"
                ]
            }
        },
        "strictNullChecks": {
            "description": "Enable strict null checks. Requires TypeScript version 2.0 or later.",
            "type": "boolean"
        },
        "maxNodeModuleJsDepth": {
            "description": "The maximum dependency depth to search under node_modules and load JavaScript files. Only applicable with --allowJs.",
            "type": "number",
            "default": 0
        },
        "importHelpers": {
            "description": "Import emit helpers (e.g. '__extends', '__rest', etc..) from tslib. Requires TypeScript version 2.1 or later.",
            "type": "boolean"
        },
        "jsxFactory": {
            "description": "Specify the JSX factory function to use when targeting react JSX emit, e.g. 'React.createElement' or 'h'. Requires TypeScript version 2.1 or later.",
            "type": "string",
            "default": "React.createElement"
        },
        "alwaysStrict": {
            "description": "Parse in strict mode and emit 'use strict' for each source file. Requires TypeScript version 2.1 or later.",
            "type": "boolean"
        },
        "strict": {
            "description": "Enable all strict type checking options. Requires TypeScript version 2.3 or later.",
            "type": "boolean"
        },
        "downlevelIteration": {
            "description": "Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. Requires TypeScript version 2.3 or later.",
            "type": "boolean"
        },
        "checkJs": {
            "description": "Report errors in .js files. Requires TypeScript version 2.3 or later.",
                "type": "boolean"
        }
    }
}

compileOptions : type

{
    "type": "object",
    "description": "Instructs the TypeScript compiler how to compile .ts files.",
    "properties": {
        "typeRoots": {
            "description": "Specify list of directories for type definition files to be included. Requires TypeScript version 2.0 or later.",
            "type": "array",
            "items": {
                "type": "string"
            }
        },
        "types": {
            "description": "Type declaration files to be included in compilation. Requires TypeScript version 2.0 or later.",
            "type": "array",
            "items": {
                "type": "string"
            }
        }
    }
}

@types

  • TypeScript 2.0 부터 사용 가능해진 내장 type definition 시스템
  • 아무 설정을 안하면 ?
    • node_modules/@types 라는 모든 경로를 찾아서 사용
  • typeRoots 를 사용하면 ?
    • 배열 안에 들어있는 경로들 아래서만 가져옵니다.
  • types 를 사용하면 ?
    • 배열 안의 모듈 혹은 ./node_modules/@types/ 안의 모듈 이름에서 찾아옵니다.
    • [] 빈 배열을 넣는다는건 이 시스템을 이용하지 않겠다는 것입니다.
  • typeRoots 와 types 를 같이 사용하지 않습니다.

compileOptions : target 과 lib

{
    "type": "object",
    "description": "Instructs the TypeScript compiler how to compile .ts files.",
    "properties": {
        "target": {
            "description": "Specify ECMAScript target version. Permitted values are 'es3', 'es5', 'es2015', 'es2016', 'es2017' or 'esnext'.",
            "type": "string",
            "default": "es3",
            "anyOf": [
                {
                    "enum": [
                        "es3",
                        "es5",
                        "es2015",
                        "es2016",
                        "es2017",
                        "esnext"
                      ]
                }, {
                      "pattern": "^([eE][sS]([356]|(201[567])|[nN][eE][xX][tT]))$"
                }
            ]
        },
        "lib": {
            "description": "Specify library file to be included in the compilation. Requires TypeScript version 2.0 or later.",
            "type": "array",
            "items": {
                "type": "string",
                "enum": [ "es5", "es6", "es2015", "es7", "es2016", "es2017", "esnext", "dom", "dom.iterable", "webworker", "scripthost", "es2015.core", "es2015.collection", "es2015.generator", "es2015.iterable", "es2015.promise", "es2015.proxy", "es2015.reflect", "es2015.symbol", "es2015.symbol.wellknown", "es2016.array.include", "es2017.object", "es2017.sharedmemory", "esnext.asynciterable"
                ]
            }
        },
        "noLib": {
            "description": "Do not include the default library file (lib.d.ts).",
            "type": "boolean"
        }
    }
}

target 과 lib

  • target
    • 빌드의 결과물을 어떤 버전으로 할 것이냐
    • 지정을 안하면 es3 입니다.
  • lib
    • 기본 type definition 라이브러리를 어떤 것을 사용할 것이냐
    • lib 를 지정하지 않을 때,
      • target 이 'es3' 이고, 디폴트로 lib.d.ts 를 사용합니다.
      • target 이 'es5' 이면, 디폴트로 dom, es5, scripthost 를 사용합니다.
      • target 이 'es6' 이면, 디폴트로 dom, es6, dom.iterable, scripthost 를 사용합니다.
    • ​lib 를 지정하면 그 lib 배열로만 라이브러리를 사용하니다.
      • ​빈 [] => 'no definition found 어쩌구'

compileOptions : outDir, outFile 

{
    "type": "object",
    "description": "Instructs the TypeScript compiler how to compile .ts files.",
    "properties": {
        "outFile": {
            "description": "Concatenate and emit output to single file.",
            "type": "string"
        },
        "outDir": {
            "description": "Redirect output structure to the directory.",
            "type": "string"
        },
        "rootDir": {
            "description": "Specifies the root directory of input files. Use to control the output directory structure with --outDir.",
            "type": "string"
        }
    }
}

compileOptions : module

{
    "type": "object",
    "description": "Instructs the TypeScript compiler how to compile .ts files.",
    "properties": {
        "module": {
            "description": "Specify module code generation: 'none', 'CommonJS', 'Amd', 'System', 'UMD', or 'es2015'.",
            "enum": [ "commonjs", "amd", "umd", "system", "es6", "es2015", "none" ]
        },
        "moduleResolution": {
            "description": "Specifies module resolution strategy: 'node' (Node) or 'classic' (TypeScript pre 1.6) .",
            "type": "string",
            "pattern": "^(([Nn]ode)|([Cc]lassic))$",
            "default": "classic"
        },
        "baseUrl": {
            "description": "Base directory to resolve non-relative module names.",
            "type": "string"
        },
        "paths": {
            "description": "Specify path mapping to be computed relative to baseUrl option.",
            "type": "object"
        },
        "rootDirs": {
            "description": "Specify list of root directories to be used when resolving modules.",
            "type": "array",
            "items": {
                "type": "string"
            }
        }
    }
}

module

  • module
    • 컴파일 된 모듈의 결과물을 어떤 모듈 시스템으로 할지를 결정
    • target 이 'es6' 이면 es6 가 디폴트이고,
    • target 이 'es6' 가 아니면 commonjs 가 디폴트 입니다.
    • AMD 나 System 와 사용하려면, outFile 이 지정되어야 합니다.
    • ES6 나 ES2015 를 사용하려면, target 이 es5 이하여야 합니다.
  • moduleResolution
    • ts 소스에서 모듈을 사용하는 방식을 지정해야 합니다.
    • Classic 아니면 Node 입니다.
    • CommonJS 일때만 Node 라고 생각하시면 됩니다.
  • paths 와 baseUrl
    • 상대경로 방식이 아닌 baseUrl 로 꼭지점과 paths 안의 키/밸류로 모듈을 가져가는 방식입니다.
  • rootDirs : 배열 안에서 상대 경로를 찾는 방식입니다.

Chapter 5-1. TypeScript Basic Types

  • TypeScript 에서 프로그램 작성을 위해 기본 제공하는 데이터 타입
  • 사용자가 만든 타입은 결국은 이 기본 자료형들로 쪼개집니다.
  • JavaScript 기본 자료형을 포함 (superset)
    • ECMAScript 표준에 따른 기본 자료형은 6가지
      • Boolean
      • Number
      • String
      • Null
      • Undefined
      • Symbol (ECMAScript 6 에 추가)
      • Array : object 형
  • 프로그래밍을 도울 몇가지 타입이 더 제공된다.
    • Any
    • Void
    • Never
    • Enum
    • Tuple : object 형

Primitive Type

  • 오브젝트와 레퍼런스 형태가 아닌 실제 값을 저장하는 자료형입니다.

  • 프리미티브 형의 내장 함수를 사용 가능한것은 자바스크립트 처리 방식 덕분

let name = 'mark';

name.toString();

literal ?

  • 값자체가 변하지 않는 값을 의미합니다.

  • 상수와 다른 것은 상수는 가리키는 포인터가 고정이라는 것이고, 리터럴은 그 자체가 값이자 그릇입니다.

35; // number 리터럴
'mark' // string 리터럴
true // boolean 리터럴

// object 리터럴
{
    name: 'mark',
    age: 35
}

literal ?

  • 값자체가 변하지 않는 값을 의미합니다.

  • 상수와 다른 것은 상수는 가리키는 포인터가 고정이라는 것이고, 리터럴은 그 자체가 값이자 그릇입니다.

  • "리터럴  상수는 5, 1.23   과  같은  숫자나, 과 같은 문자열 등을 말합니다. 'This  is  a  string'   혹은 "It’s  a  string!"이것들이  리터럴  상수라고  불리우는  이유는  이것들이  프로그램  내에  직접  문자  형태로(literally)지정되는  값들이기  때문입니다.  이러한  값들은  한번  지정되면  변하지  않습니다.  예를  들면  숫자2   는  언제나  자기  자신이  2라는  숫자임을  나타내며  어떤  다른  의미도  갖지  않습니다.  이들은  한번  지정되면  그  값을  변경할  수  없기  때문에  _상수_입니다.  그  중에서도  특별히  이러한  값들을  리터럴 상수라고 부릅니다." @kangpual

Boolean / boolean

  • 가장 기본적인 데이터 타입

  • 단순한 true 혹은 false 값 입니다.

  • JavaScript / TypeScript 에서 'boolean' 이라고 부른다.

let isDone: boolean = false;

typeof isDone === 'boolean' // true

// Type 'boolean' is assignable to type 'Boolean'.
let isOk: Boolean = true;

// Type 'Boolean' is not assignable to type 'boolean'.
// 'boolean' is a primitive, but 'Boolean' is a wrapper object.
// Prefer using 'boolean' when possible.
let isNotOk: boolean = new Boolean(true);

Number / number

  • JavaScript 와 같이, TypeScript 의 모든 숫자는 부동 소수점 값 입니다.

  • TypeScript는 16진수 및 10진수 리터럴 외에도, ECMAScript 2015에 도입된 2진수 및 8진수를 지원합니다.

let decimal: number = 6; // 10진수 리터럴

let hex: number = 0xf00d; // 16진수 리터럴

let binary: number = 0b1010; // 2진수 리터럴

let octal: number = 0o744; // 8진수 리터럴

String / string

  • 다른 언어에서와 마찬가지로이 텍스트 형식을 참조하기 위해 `string` 형식을 사용합니다.

  • JavaScript 와 마찬가지로, TypeScript는 문자열 데이터를 둘러싸기 위해 큰 따옴표 ( " ) 나, 작은 따옴표 ( ' ) 를 사용합니다.

let name: string = "mark";

name = 'anna';

Template String

  • 행에 걸쳐 있거나, 표현식을 넣을 수 있는 문자열

  • 이 문자열은 backtick (= backquote) 기호에 둘러쌓여 있습니다.

  • 포함된 표현식은 `${ expr }` 와 같은 형태로 사용합니다.

let fullName: string = `Bob Bobbington`;
let age: number = 37;

let sentence: string = `Hello, my name is ${ fullName }.

I'll be ${ age + 1 } years old next month.`;

// template string 을 사용하지 않을 경우
let sentence: string = "Hello, my name is " + fullName + ".\n\n" +
    "I'll be " + (age + 1) + " years old next month.";

Undefined & Null

  • TypeScript 에서 'undefined' 와 'null' 은 실제로 각각 'undefined' 와 'null' 이라는 고유한 타입을가집니다.

  • 'void' 와 마찬가지로, undefined 와 null 은 그 자체로는 쓸모가 없습니다.

  • 둘다 소문자만 존재합니다.

// 이 변수들에 할당할 수 있는 것들은 거의 없다.

let u: undefined = undefined;
let n: null = null;

undefined & null are subtypes of all other types.

  • 기본 설정이 그렇습니다.

  • number 에 null 또는 undefined 를 할당할 수 있다는 의미입니다.

  • 하지만, 컴파일 옵션에서 `--strictNullChecks`사용하면, null 과 undefined 는 void 나 자기 자신들에게만 할당할 수 있습니다.

    • 이 경우, null 과 undefined 를 할당할 수 있게 하려면, union type 을 이용해야 합니다.

let name: string = null;
let age: number = undefined;

// strictNullChecks => true
// Type 'null' is not assignable to type 'string'.
let name: string = null; (X)

// null => null || void, undefined => undefined || void
// Type 'null' is not assignable to type 'undefined'.
let u: undefined = null; // (X)

let v: void = undefined; // (O)

let union: string | null | undefined = 'str';

null in JavaScript 

  • null 이라는 값으로 할당된 것을 null 이라고 합니다.

  • 무언가가 있는데, 사용할 준비가 덜 된 상태.

  • null 이라는 타입은 null 이라는 값만 가질 수 있습니다.

  • 런타임에서 typeof 연산자를 이용해서 알아내면, object 입니다.

let n: null = null;

console.log(n); // null
console.log(typeof n); // object

undefined in JavaScript 

  • 값을 할당하지 않은 변수는 undefined 라는 값을 가집니다.

  • 무언가가 아예 준비가 안된 상태

  • object 의 property 가 없을 때도 undefined 입니다.

  • 런타임에서 typeof 연산자를 이용해서 알아내면, undefined 입니다.

let u: undefined = undefined;

console.log(u); // undefined
console.log(typeof u); // undefined

Void

  • 타입이 없는 상태입니다.

  • `any` 와 반대의 의미를 가집니다.

  • Void 는 없습니다. 소문자입니다.

  • 주로 함수의 리턴이 없을 때 사용합니다. 그 외에는 사용할 일이 거의 없습니다.

function returnVoid(message): void {
    console.log(message);
}

returnVoid('리턴이 없다');

Any

  • 어떤 타입이어도 상관없는 타입입니다.

  • 이걸 최대한 쓰지 않는게 핵심입니다.

  • 왜냐면 컴파일 타임에 타입 체크가 정상적으로 이뤄지지 않기 때문입니다.

  • 그래서 컴파일 옵션 중에는 any 를 쓰면 오류를 뱉도록 하는 옵션도 있습니다.

    • noImplicitAny

function returnAny(message): any {
    console.log(message);
}

returnVoid('리턴은 아무거나');

Never

  • 리턴에 주로 사용됩니다.

  • 아래 3가지 정도의 경우가 대부분입니다.

// Function returning never must have unreachable end point
function error(message: string): never {
    throw new Error(message);
}

// Inferred return type is never
function fail() {
    return error("Something failed");
}

// Function returning never must have unreachable end point
function infiniteLoop(): never {
    while (true) {
    }
}

Array

  • 원래 자바스크립트에서 array 는 객체입니다.

  • 사용방법

    • Array<타입>

    • 타입[]

let list: number[] = [1, 2, 3];

let list: Array<number> = [1, 2, 3];

Tuple

  • 배열인데 타입이 한가지가 아닌 경우

  • 마찬가지로 객체입니다.

  • 꺼내 사용할때 주의가 필요합니다.

    • 배열을 Destructuting 하면 타입이 제대로 얻어집니다.

// Declare a tuple type
let x: [string, number];
// Initialize it
x = ["hello", 10]; // OK
// Initialize it incorrectly
x = [10, "hello"]; // Error

x[3] = "world"; // OK, 'string' can be assigned to 'string | number'

console.log(x[5].toString()); // OK, 'string' and 'number' both have 'toString'

x[6] = true; // Error, 'boolean' isn't 'string | number'

const person: [string, number] = ['mark', 35];

const [name, age] = person;

Enum

  • C 에서 보던것과 같습니다.

  • 아래 예제만 이해하면 사용 준비 끝

enum Color {Red, Green, Blue}
let c: Color = Color.Green;

enum Color {Red = 1, Green, Blue}
let c: Color = Color.Green;

enum Color {Red = 1, Green = 2, Blue = 4}
let c: Color = Color.Green;

enum Color {Red = 1, Green, Blue}
let colorName: string = Color[2];

Symbol

  • ECMAScript 2015 의 Symbol 입니다.

  • 프리미티브 타입의 값을 담아서 사용합니다.

  • 고유하고 수정불가능한 값으로 만들어줍니다.

  • 그래서 주로 접근을 제어하는데 쓰는 경우가 많았습니다.

let sym = Symbol();

let obj = {
    [sym]: "value"
};

console.log(obj[sym]); // "value"

Chapter 5-2. var, let, const

var VS let, const

  • var
    • ES5
    • 변수의 유효 범위 : 함수 스코프
    • 호이스팅 (O)
    • 재선언 가능
  • let, const
    • ES6
    • 변수의 유효 범위 : 블록 스코프 (친숙)
    • 호이스팅 (X)
    • 재선언 불가
  • var 말고 let, const

Hoisting

console.log(hoisted_var);

var hoisted_var = '변수를 아래서 선언했는데 사용이 위에서 가능';

////////////////////////////////////////

console.log(hoisted_let);

let hoisted_let = '변수를 아래서 선언했는데 사용이 위에서 불가';

redeclare

var redeclare_var: string = '한번 선언 했는데';
var redeclare_var: string = '또 선언이 가능';
// var redeclare_var: number = 0; (X)

////////////////////////////////////////

let redeclare_let = '한번 선언 했기 때문에';
let redeclare_let = '또 선언이 불가';

/*

그렇지만 var 에서 재선언 하더라도 같은 타입이어야 함.

*/

let 과 const 의 타입 추론

let a: string = '에이';
let b = '비이';

const c: string = '씨이';
const d = '디이';

/*

1. a 는 명시적으로 지정된 타입인 string
2. b 는 타입추론에 의한 타입인 string
3. c 는 명시적으로 지정된 타입인 string
4. d 는 타입추론에 의한 타입인 리터럴타입 "디이"

*/

Chapter 5-3. Type assersions

타입 어설션

  • 적당한 번역을 찾을 수가 없었습니다.
    • Type assertions
  • 형변환과는 다릅니다.
    • 형변환은 실제 데이터 구조를 바꿔줍니다.
  • '타입이 이것이다' 라고 컴파일러에게 알려주는 것을 의미합니다.
    • 그래서 행동에 대해서 작성자가 100% 신뢰하는 것이 중요합니다.
  • 문법적으로는 두가지 방법이 있습니다.
    • 변수 as 강제할타입
    • <강제할타입>변수

sample code

let someValue: any = "this is a string";

let strLength: number = (<string>someValue).length;
let strLength: number = (someValue as string).length;

/*

1. 주로 넓은 타입에서 좁은 타입으로 강제하는 경우가 많다.
2. jsx 에서는 as 를 쓴다.

*/

Chapter 6-1. Type alias

타입 별칭 (별명)

  • 인터페이스랑 비슷해 보입니다.

  • Primitive, Union Type, Tuple

  • 기타 직접 작성해야하는 타입을 다른 이름을 지정할 수 있습니다.

  • 만들어진 타입의 refer 로 사용하는 것이지 타입을 만드는것은 아닙니다.

Aliasing Primitive

type MyStringType = string;

const str = 'world';

let myStr: MyStringType = 'hello';
myStr = str;

/*

별 의미가 없다..

*/

Aliasing Union Type

let person: string | number = 0;
person = 'Mark';

type StringOrNumber = string | number;

let another: StringOrNumber = 0;
another = 'Anna';

/*

1. 유니온 타입은 A 도 가능하고 B 도 가능한 타입
2. 길게 쓰는걸 짧게

*/

Aliasing Tuple

let person: [string, number] = ['Mark', 35];

type PersonTuple = [string, number];

let another: PersonTuple = ['Anna', 24];

/*

1. 튜플 타입에 별칭을 줘서 여러군데서 사용할 수 있게 한다.

*/

Quiz

Interface 와의 차이점 (1)

type Alias = { num: number }

interface Interface {
    num: number;
}

declare function aliased(arg: Alias): Alias;
declare function interfaced(arg: Interface): Interface;

/*

1. type alias 는 object literal type 로
2. interface 는 interface 로

*/

Interface 와의 차이점 (2)

type PersonAlias = {
    name: string;
    age: number;
};

interface IPerson extends PersonAlias {

}

let ip: IPerson = {
    name: 'Mark',
    age: 35
};

class PersonImpl implements PersonAlias {
    name: string;
    age: number;

    hello() {
        console.log('안녕하세요');
    }
}

let pi: PersonImpl = new PersonImpl();
pi.hello();

class PersonChild extends PersonAlias {

}

/*

1. 당연한건 type alias 끼리는 extends, implements 불가
2. interface extends type alias 가능
3. class implements type alias 가능
4. class extends type alias 블가 (interface 도 마찬가지)
5. 마치 interface 처럼 동작한다.

*/

Chapter 6-2. Interface

interface - basic

function hello(person: { name: string; age: number; }): void {
    console.log(`안녕하세요! ${person.name} 입니다.`);
}

const p: { name: string; age: number; } = {
    name: 'Mark',
    age: 35
};

hello(p); // 안녕하세요! Mark 입니다.

///////////////////////////////////////////////////////////////

interface Person {
    name: string;
    age: number;
}

function hello(person: Person): void {
    console.log(`안녕하세요! ${person.name} 입니다.`);
}

const p: Person = {
    name: 'Mark',
    age: 35
};

hello(p); // 안녕하세요! Mark 입니다.

interface - optional property (1)

interface Person {
    name: string;
    age?: number;
}

function hello(person: Person): void {
    console.log(`안녕하세요! ${person.name} 입니다.`);
}

const p1: Person = {
    name: 'Mark',
    age: 35
};

const p2: Person = {
    name: 'Anna'
};

hello(p1); // 안녕하세요! Mark 입니다.
hello(p2); // 안녕하세요! Anna 입니다.

interface - optional property (2)

interface Person {
    name: string;
    age?: number;
    [props: string]: any;
}

function hello(person: Person): void {
    console.log(`안녕하세요! ${person.name} 입니다.`);
}

const p1: Person = {
    name: 'Mark',
    age: 35,
};

const p2: Person = {
    name: 'Anna',
    systers: [
        'Sung',
        'Chan'
    ]
};

const p3: Person = {
    name: 'Bokdaengi',
    father: p1,
    mother: p2
};

hello(p1); // 안녕하세요! Mark 입니다.
hello(p2); // 안녕하세요! Anna 입니다.
hello(p3); // 안녕하세요! Bokdaengi 입니다.

interface - function in interface

interface Person {
    name: string;
    age: number;
    hello(): void;
}

const p1: Person = {
    name: 'Mark',
    age: 35,
    hello: function (): void {
        console.log(this);
        console.log(`안녕하세요! ${this.name} 입니다.`);
    }
};

const p2: Person = {
    name: 'Mark',
    age: 35,
    hello(): void {
        console.log(this);
        console.log(`안녕하세요! ${this.name} 입니다.`);
    }
};

const p3: Person = {
    name: 'Mark',
    age: 35,
    hello: (): void => {
        console.log(this);
        console.log(`안녕하세요! ${this.name} 입니다.`);
    }
};

p1.hello(); // 안녕하세요! Mark 입니다.
p2.hello(); // 안녕하세요! Mark 입니다.
p3.hello(); // 안녕하세요! 입니다.

class implements interface

interface IPerson {
    name: string;
    age?: number;
    hello(): void;
}

class Person implements IPerson {
    name: string;

    constructor(name: string) {
        this.name = name;
    }

    hello(): void {
        console.log(`안녕하세요! ${this.name} 입니다.`);
    }
}

const person = new Person('Mark');
person.hello(); // 안녕하세요! Mark 입니다.

interface extends interface

interface Person {
    name: string;
    age?: number;
}

interface Korean extends Person {
    city: string;
}

const k: Korean = {
    name: '이웅재',
    city: '서울'
};

function interface

interface HelloPerson {
    // (name: string, age: number): void;
    (name: string, age?: number): void;
}

let helloPerson: HelloPerson = function (name: string) {
    console.log(`안녕하세요! ${name} 입니다.`);
};

helloPerson('Mark'); // 안녕하세요! Mark 입니다.

/*

함수의 타입 체크는 할당할때가 아니라 사용할때 한다는 점을 명심

*/

Chapter 6-3. Indexable Types

string OR number

interface StringArray {
    [index: number]: string;
}

const sa: StringArray = {}; // 옵셔널하다
sa[100] = '백';

interface StringDictionary {
    [index: string]: string;
}

const sd: StringDictionary = {}; // 옵셔널하다
sd.hundred = '백';

interface StringArrayDictionary {
    [index: number]: string;
    [index: string]: string;
}

const sad: StringArrayDictionary = {};
// 당연히 옵셔널하다.
sad[100] = '백';
sad.hundred = '백';

string index = optional property

interface StringDictionary {
    [index: string]: string;
    name: string;
}

const sd: StringDictionary = {
    name: '이름' // 필수
};

sd.any = 'any'; // 어떤 프로퍼티도 가능

////////////////////////////////////////////////

interface StringDictionaryNo {
    [index: string]: string;
    // name: number; // (X) 인덱서블 타입이 string 값을 가지기 때문에 number 를 필수로 끌어오면 에러
}

Chapter 7. Class

클래스 만들기

class Person {
    name: string;
    age: number;
}

const person: Person = new Person();
console.log(person); // Person {}
person.age = 35;
console.log(person.name); // undefined

/*

1. 생성자 함수가 없으면, 디폴트 생성자가 불린다.

2. 클래스의 프로퍼티 혹은 멤버 변수가 정의되어 있지만, 값을 대입하지 않으면 undefined 이다.
=> 오브젝트에 프로퍼티가 아예 존재하지 않는다.

3. 접근제어자 (Access Modifier) 는 public 이 디폴트 이다.

*/

클래스와 프로퍼티 (1)

class Person {
    name: string;
    age: number;

    constructor() {
        console.log(this.name === null); // false
        console.log(this.name === undefined); // true
    }
}

const person: Person = new Person();
person.name = 'Mark';
person.age = 35;
console.log(person); // Person {name: 'mark', age: 35}

클래스와 프로퍼티 (2)

class Person {
    name: string = 'Mark';
    age: number = 35;

    constructor() {
        console.log(this.name); // 'mark'
    }
}

const person: Person = new Person();
console.log(person); // Person {name: 'Mark', age: 35}

/*

1. 클래스의 프로퍼티를 선언과 동시에 값을 할당하는 방법도 있다.

2. 생성자가 불리기 전에 이미 프로퍼티의 값이 저장되어 있음을 알 수 있다.

*/

클래스와 프로퍼티의 접근 제어자 (1)

class Person {
    public name: string;
    private _age: number;

    constructor(age: number) {
        this._age = age;
    }
}

const person: Person = new Person(35);
person.name = 'Mark';
// person._age (X)
console.log(person); // Person {name: 'Mark', _age: 35}

/*

1. private 으로 설정된 프로퍼티는 dot 으로 접근할 수 없다.

2. 클래스 내부에서는 private 프로퍼티를 사용할 수 있다.

3. private 이 붙은 변수나 함수는 _ 를 이름앞에 붙이는데,
이는 문법이 아니라 널리 쓰이는 코딩 컨벤션이다.

*/

클래스와 프로퍼티의 접근 제어자 (2)

class Parent {
    private privateProp: string;
    protected protectedProp: string;

    constructor() {

    }
}

class Child extends Parent {
    constructor() {
        super();

        this.protectedProp = 'protected';
        // this.privateProp = 'private'; // (X)
    }
}

/*

1. 부모에서 private 으로 설정된 프로퍼티는 상속을 받은 자식에서도 접근할 수 없다.

2. 부모에서 protected 로 설정된 프로퍼티는 상속을 받은 자식에서 접근이 가능하다.

3. 상속을 받은 자식 클래스에서 부모 클래스에 this 를 통해 접근하려면, 생성자에서 super(); 를 통해 초기화 해야한다.

*/

클래스와 디폴트 생성자

class Person {
    public name: string;
    private _age: number;

    constructor(age: number) {
        this._age = age;
    }
}

const person: Person = new Person();

/*

1. 디폴트 생성자는 프로그래머가 만든 생성자가 없을 때 사용할 수 있다.
=> 사용자가 만든 생성자가 하나라도 있으면, 디폴트 생성자는 사라진다.

*/

클래스와 메서드

class Person {
    constructor(private _name: string, private _age: number) { }

    print(): void {
        console.log(`이름은 ${this._name} 이고, 나이는 ${this._age} 살 입니다.`);
    }

    printName = (): void => {
        console.log(`이름은 ${this._name} 입니다.`);
    }

    private printAge(): void  {
        console.log(`나이는 ${this._age} 살 입니다.`);
    }
}

const person: Person = new Person('Mark', 35);
person.print(); // 이름은 Mark 이고, 나이는 35 살 입니다.
person.printName(); // 이름은 Mark 입니다.
// person.printAge(); // (X)

/*

1. 클래스 내부에 작성된 메서드는 public 이 디폴트
2. arrow function 으로 작성 가능
3. private 을 이용하면 클래스 외부애서 접근 불가

*/

클래스와 상속 (1)

class Parent {
    constructor(protected _name: string, protected _age: number) { }

    print(): void {
        console.log(`이름은 ${this._name} 이고, 나이는 ${this._age} 살 입니다.`);
    }

    printName = (): void => {
        console.log(`이름은 ${this._name} 입니다.`);
    }

    private printAge(): void  {
        console.log(`나이는 ${this._age} 살 입니다.`);
    }
}

class Child extends Parent {
    _name = 'Mark Jr.';
}

// const p: Child = new Child(); // (X)
const p: Child = new Child('', 5);
p.print(); // 이름은 Son 이고, 나이는 5 살 입니다.

/*

1. 상속은 extends 키워드를 이용한다.
2. 자식 클래스에서 디폴트 생성자는 부모의 생성자와 입력 형태가 같다.

*/

클래스와 상속 (2)

class Parent {
    constructor(protected _name: string, private _age: number) { }

    print(): void {
        console.log(`이름은 ${this._name} 이고, 나이는 ${this._age} 살 입니다.`);
    }

    protected printName = (): void => {
        console.log(`이름은 ${this._name} 입니다.`);
    }

    protected printAge(): void  {
        console.log(`나이는 ${this._age} 살 입니다.`);
    }
}

class Child extends Parent {
    constructor(age: number) {
        super('Mark Jr.', age);

        this.printName();
        this.printAge();
    }
}

const p: Child = new Child(1);
// 이름은 Son 입니다.
// 나이는 1 살 입니다.

/*

1. 생성자를 정의하고, this 를 사용하려면, super 를 통해 부모의 생성자를 호출해줘야 한다.
2. super 를 호출할때는 부모 생성자의 입력 타입이 같아야 한다.
3. super 를 호출하는 것은 클래스 외부에서 호출하는 것과 같다.
4. protected 함수를 호출해서 그 안의 private 을 출력하는 것에 주의한다.

*/

클래스와 getter, setter

class Person {
    private _name: string;
    private _age: number;
    
    constructor(name: string, age: number) {
        this._name = name;
        this._age = age;
     }

    get name() {
        return this._name;
    }

    set name(name: string) {
        // 작업
        this._name = `${name} Lee`;
    }
}

const person: Person = new Person('Mark', 35);

console.log(person.name);
person.name = 'Woongjae';
console.log(person.name);

/*

1. _ 를 변수명 앞에 붙이고, 내부에서만 사용한다.
2. getter 를 함수처럼 설정하면, 프로퍼티처럼 꺼내쓸수있다.
3. 마찬가지로 setter 를 함수처럼 설정하면, 추가 작업을 하고 셋팅할 수 있다.

*/

클래스와 static 프로퍼티 => 클래스 멤버 변수

class Person {
    public static CITY = "";
    private static lastName: string = 'Lee';
    private _name: string;
    private _age: number;
    
    constructor(name: string, age: number) {
        this._name = name;
        this._age = age;
     }

    public print() {
        console.log(`${this._name} ${Person.lastName} in ${Person.CITY}.`);
    }
}

const person: Person = new Person('Mark', 35);
Person.CITY = 'Seoul';
person.print(); // Mark Lee in Seoul.

/*

1. static 키워드를 붙힌 프로퍼티는 클래스.프로퍼티로 사용한다.
2. static 프로퍼티에 private, protected 를 붙히면 똑같이 동작한다.

*/

클래스와 static 메서드 => 클래스 멤버 함수

class Person {
    public static Talk(): void {
        console.log('안녕하세요.');
    }
}

Person.Talk(); // 안녕하세요.

모듈에서 private static 프로퍼티 혹은 메서드

class Person {
    private static PROPERTY = '프라이빗 프로퍼티';
    private static METHOD() {
        console.log('프라이빗 메서드');
    }

    constructor() {
        console.log(Person.PROPERTY);
        Person.METHOD();
    }
}

//////////////////////////////////////////////

const PROPERTY = '모듈 내 변수';
function METHOD() {
    console.log('모듈 내 함수');
}

export class Person {
    constructor() {
        console.log(PROPERTY);
        METHOD();
    }
}

Abstract Class

abstract class APerson {
    protected _name: string = 'Mark';
    abstract setName(name: string): void; 
}

class Person extends APerson {
    setName(name: string): void {
        this._name = name;
    }
}

// const person = new APerson(); // (X)
const person = new Person();

/*

1. abstract 키워드가 사용된 클래스는 new 로 생성할 수 없다.
2. abstract 키워드가 사용된 클래스를 상속하면 abstract 키워드가 붙은 함수를 구현해야 한다.

*/

Class 와 private constructor

class Preference {
    private constructor() {

    }
}

// const p: Preference = new Preference(); (X)

/*

1. 생성자 함수 앞에 접근제어자인 private 을 붙일 수 있다.
2. 외부에서 생성이 불가능하다.

*/

Class 와 싱글톤 패턴

class Preference {
    public static getInstance() {
        if (Preference.instance === null) {
            Preference.instance = new Preference();
        }

        return Preference.instance;
    }
    private static instance: Preference = null;
    private constructor() {

    }
}

const p: Preference = Preference.getInstance();

/*

1. private 생성자를 이용해서 내부에서만 인스턴스 생성이 가능하도록 함.
2. pubilc static 메서드를 통해 private static 인스턴스 레퍼런스를 획득한다.
3. Lazy Loading (Initialization) : 최초 실행시가 아니라, 사용시에 할당을 함

*/

Class 와 readonly

class Person {
    private readonly _name: string = null;
    public readonly age: number = 35;

    constructor(name: string) {
        this._name = name;
    }

    public setName(name: string) {
        // this._name = name; (X)
    }
}

const p: Person = new Person('Mark');
console.log(p.age);
// p.age = 36; // (X)

/*

1. private readonly 로 선언된 경우, 생성자에서는 할당이 가능하다.
2. private readonly 로 선언된 경우, 생성자 이외에서는 할당이 불가능하다.
3. public readonly 로 선언된 경우, 클래스 외부에서는 다른값을 할당할 수 없다.
4. 마치 getter 만 있는 경우와 같다.

*/

Exercise (1)

function Car(name) {
    this.name = name;
    this.speed = 0;
 
    this.honk = function() {
        console.log("부우우웅");
    };
 
    this.accelerate = function(speed) {
        this.speed = this.speed + speed;
    }
}

var car = new Car("BENZ");
car.honk();
console.log(car.speed);
car.accelerate(10);
console.log(car.speed);

Exercise (2)

var baseObject = {
    width: 0,
    length: 0
};

var rectangle = Object.create(baseObject);
rectangle.width = 8;
rectangle.length = 6;
rectangle.area = function() {
    return this.width * this.length;
};

console.log(rectangle.area());

Exercise (3)

var person = {
    _firstName: ""
};

// https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
// ES5 이상
Object.defineProperty(person, "firstName", {
    get: function () {
        return this._firstName;
    },
    set: function (value) {
        if (value.length > 3) {
            this._firstName = value;
        }
        else {
            this._firstName = "";
        }
    },
    enumerable: true,
    configurable: true
});

console.log(person.firstName);
person.firstName = "Ma";
console.log(person.firstName);
person.firstName = "Maximilian";
console.log(person.firstName);

Chapter 8. Generic

any => generic

function helloString(message: string): string {
    return message;
}

function helloNumber(message: number): number {
    return message;
}

// 더 많은 반복된 함수들 ...

function hello(message: any): any {
    return message;
}

function helloGeneric<T>(message: T): T {
    return message;
}

console.log(hello('Mark').length);
console.log(hello(35).length);

console.log(helloGeneric(35).toString()); // console.log(helloGeneric<number>(35).toString());

// hello 의 리턴이 any 이기 때문에 타입 헬퍼가 제대로 되지 않음
// helloGeneric 을 사용하면 정상적으로 사용가능

basic generic

function helloGeneric<T>(message: T): T {
    return message;
}

function hello<T>(message: T): T {
    return message;
}

console.log(hello<string>('Hello'));
let age = hello(35);
hello<number>('35');

/*

1. Generic 타입을 쓰지 않으면, T 로 추론
2. Generic 타입을 쓰면, T 를 확인

*/

Generic Array

function hello<T>(messages: T[]): T {
    return messages[0];
}

console.log(hello<string>(['Hello', 'World']));

/*

hello 함수의 제네릭 타입을 [] 를 이용하여 배열로 사용할 수 있음

*/

Generic Types

type HelloGeneric = <T>(message: T) => T;

const hello: HelloGeneric = <T>(message: T): T => {
    return message;
}

console.log(hello<string>('Hello').length);

/*

구현체에 return T 를 설정하지 않아도,
return false 시 오류가 나지 않는다?

*/

Generic Class

class Person<T> {
    private _name: T;
    private _age: number;

    constructor(name: T) {
        this._name = name;
    }
}

new Person('Mark');
// new Person<string>(35);

/*

명시적으로 제네릭 타입을 설정하면 오류

*/

Generic with extends

class Person<T extends string | number> {
    private _name: T;
    private _age: T;

    constructor(name: T) {
        this._name = name;
    }
}

new Person('Mark');
new Person(35);
// new Person(true);

/*

T 가 string 또는 number 를 상속받기 때문에 boolean 은 안된다.

*/

Generic with multiple types

class Person<T, K> {
    private _name: T;
    private _age: K;

    constructor(name: T, age: K) {
        this._name = name;
        this._age = age;
    }
}

new Person('Mark', 35);

type lookup system

interface Person {
    name: string;
    age: number;
}

const person: Person = {
    name: 'Mark',
    age: 35
};

function getProperty<T, K extends keyof T>(obj: T, key: K): T[K] {
    return obj[key];
}

function setProperty<T, K extends keyof T>(obj: T, key: K, value: T[K]): void {
    obj[key] = value;
}

console.log(getProperty(person, 'name'));
// console.log(getProperty(person, fullname));
setProperty(person, 'name', 'Anna');
console.log(getProperty(person, 'name'));
// setProperty(person, 'name', 24);

Chapter 9. iterator

for..of

  • es3
    • for (var i = 0; i < array.length; i++)
  • es5
    • array.forEach
      • return 으로 순회를 탈출할 수 없다.
  • es6
    • for (const item of array)
      • 배열에서만 사용이 가능

for..in

  • 배열을 순회할 때는 사용하지 않을 것
    • index 가 number 가 아니라 string 으로 나온다.
    • 배열의 프로퍼티를 순회할 수도 있다.
    • prototype 체인의 프로퍼티를 순회할 수도 있다.
    • 루프가 무작위로 순회할 수도 있다.
    • for..of 를 쓸 것
  • 객체를 순회할 때
    • for (const prop of Object.keys(obj)) 도 사용할 수 있다.

Example

const array = ['first', 'second'];
const obj = {
    name: 'Mark',
    age: 35
};

// 배열에 for..of 이용
for (const item of array) {
    console.log(typeof item + ', ' + item);
}

// 배열에 for..in 이용
// item 이 string 타입의 숫자
for (const item in array) {
    console.log(typeof item + ', ' + item);
}


// 객체에 for..of 이용 => 오류
/*
for (const item of obj) {
    console.log(typeof item + ', ' + item);
}
*/

// 객체에 for..in 이용
for (const item in obj) {
    console.log(typeof item + ', ' + item);
}

// 객체의 keys 들에 for..of 이용
for (const item of Object.keys(obj)) {
    console.log(typeof item + ', ' + item);
}

target es3 forEach

const array = ['first', 'second'];

// ts
array.forEach((item) => {
    console.log(item);
});

// js
array.forEach(function (item) {
    console.log(item);
});

/*

target 이 es3 인데도 forEach 는 트랜스파일이 되지 않았음.
https://github.com/Microsoft/TypeScript/issues/2410

*/

Symbol.iterator

  • 프로퍼티이며, 함수가 구현되어있으면, iterable 이라고 한다.
  • Array, Map, Set, String, Int32Array, Uint32Array, etc. 에는 내장된 구현체가 있으므로 이터러블 하다.
  • 그냥 객체는 이터러블하지 않다.
  • 이터레이터를 통해 이터러블한 객체의 Symbol.iterator 함수를 호출한다.

Symbol.iterator

  • target : es3 or es5
    • Array 에만 for..of 사용 가능
    • 일반 객체에 사용하면 오류
  • target : es6
    • Symbol.iterator 함수를 구현하면 어떤 객체에도 for..of 사용 가능

lib.es6.d.ts

interface IteratorResult<T> {
    done: boolean;
    value: T;
}

interface Iterator<T> {
    next(value?: any): IteratorResult<T>;
    return?(value?: any): IteratorResult<T>;
    throw?(e?: any): IteratorResult<T>;
}

interface Iterable<T> {
    [Symbol.iterator](): Iterator<T>;
}

interface IterableIterator<T> extends Iterator<T> {
    [Symbol.iterator](): IterableIterator<T>;
}

Custom Iterable

class CustomIterable implements Iterable<string> {
    private _array: Array<string> = ['first', 'second'];

    [Symbol.iterator]() {
        var nextIndex = 0;

        return {
            next: () => {
                return {
                    value: this._array[nextIndex++],
                    done: nextIndex > this._array.length
                }
            }
        }
    }
}

const cIterable = new CustomIterable();

for (const item of cIterable) {
    console.log(item);
}

Chapter 10. decorator

Decorator 종류

  • Class Decorator
  • Method Decorator
  • Property Decorator
  • Parameter Decorator

Decorator 코드 작성 준비

  • step1. 프로젝트 생성
    • mkdir ts-decorator
    • cd ts-decorator
    • yarn init -y
  • step2. typescript 설치
    • yarn add typescript -D
  • step3. tsconfig 설정
    • node_modules/.bin/tsc --init
    • experimentalDecorators 추가

  • ​step4. vscode 컴파일 설정

    • ${workspaceRoot}/node_modules/.bin/tsc

    • command + shift + <B>

Class Decorator Basic

function hello(constructorFn: Function) {
    console.log(constructorFn);
}

function helloFactory(show: boolean) {
    if (show) {
        return hello;
    } else {
        return null;
    }
}

// @hello
@helloFactory(true)
class Person {
    constructor() {
        console.log('new Person()');
    }
}

new Person();

/*

helloFactory 는 팩토리 스타일

*/

Class Decorator Advanced

function addHello(constructorFn: Function) {
    constructorFn.prototype.hello = function() {
        console.log('hello');
    }
}

@addHello
class Person {
    constructor() {
        console.log('new Person()');
    }
}

const person = new Person();
(<any>person).hello();

Method Decorator

function editable(canBeEdit: boolean) {

    return function(target: any, propName: string, description: PropertyDescriptor) {
        console.log(canBeEdit);
        console.log(target);
        console.log(propName);
        console.log(description);
        description.writable = canBeEdit;
    }
}

class Person {
    constructor() {
        console.log('new Person()');
    }

    @editable(true)
    hello() {
        console.log('hello');
    }
}

const person = new Person();
person.hello();
person.hello = function() {
    console.log('world');
}
person.hello();

Property Decorator

function writable(canBeWrite: boolean) {
    return function(target: any, propName: string): any {
        console.log(canBeWrite);
        console.log(target);
        console.log(propName);
        return {
            writable: canBeWrite
        }
    }
}

class Person {
    @writable(false)
    name: string = 'Mark';

    constructor() {
        console.log('new Person()');
    }
}

const person = new Person();
console.log(person.name);

/*

undefined

*/

Parameter Decorator

function printInfo(target: any, methodName: string, paramIndex: number) {
    console.log(target);
    console.log(methodName);
    console.log(paramIndex);
}

class Person {
    private _name: string;
    private _age: number;

    constructor(name: string, @printInfo age: number) {
        this._name = name;
        this._age = age;
    }

    hello(@printInfo message: string) {
        console.log(message);
    }
}

/*

Person { hello: [Function] }
hello
0
[Function: Person]
undefined
1

*/

Chapter 11. Type Inference

타입 추론

  • 기본적으로 타입을 명시적으로 쓰지 않을 때 추론하는 방법에 대한 규칙
    • 명시적으로 쓰는 것은 타입 추론이 아니라 코드를 읽기 좋게 하는 지름길
  • let 은 기본적으로 우리가 아는 기본 자료형으로 추론
  • const 는 리터럴 타입으로 추론
    • 오브젝트 타입을 타입을 쓰지 않으면, 프로퍼티는 let 처럼 추론
      • const person = {name: 'Mark', age: 35}; 면
      • person => {name: string; age: number;} 로 추론
  • 대부분은 추론이 쉽다.
    • 단순 변수
    • structuring, destructuring
  • array, 함수의 리턴에서는 원하는데로 얻기가 힘들다.

배열 타입 추론

const array1 = [];
const array2 = ['a', 'b', 'c'];
const array3 = ['a', 1, false];

class Animal {
    name: string;
}

class Dog extends Animal { 
    dog: string;
}

class Cat extends Animal {
    cat: string;
}

const array4 = [new Dog(), new Cat()];

리턴 타입 추론

function hello(message: string | number) {
    if (message === 'world') {
        return 'world';
    } else {
        return 0;
    }
}

유니온 타입과 타입 가드

interface Person {
    name: string;
    age: number;
}

interface Car {
    brand: string;
    wheel: number;
}

function isPerson(arg: any): arg is Person {
    return arg.name !== undefined;
}

function hello(arg: Person | Car) {
    if (isPerson(arg)) {
        console.log(arg.name);
        // console.log(arg.brand);
    } else {
        // console.log(arg.name);
        console.log(arg.brand);
    }
}

Chapter 12. TypeScript Roadmap

Chapter 13. Project 맛보기