Woongjae Lee
Daangn - Frontend Core Team ex) NHN Dooray - Frontend Team Leader ex) ProtoPie - Studio Team
2woongjae@gmail.com
컴파일러
C. 에디터
A. 실행 환경
B. 컴파일러
Chrome's
V8 JavaScript Engine
을 사용하여,
자바스크립트를 해석하고
OS 레벨에서의 API를 제공하는
서버사이드 용
자바스크립트 런타임 환경
HTML 을 동적으로 만들기 위해
브라우저에서
자바스크립트를 해석하고,
DOM 을 제어할 수 있도록 하는
자바스크립트 런타임 환경
// in config/base.json
{
"compilerOptions": {
"noImplicitAny": true,
"strictNullChecks": true
}
}
// in tsconfig.json
{
"extends": "./configs/base",
"files": [
"main.ts",
"supplemental.ts"
]
}
{
"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"
}
}
}
{
"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"
}
}
}
}
{
"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"
}
}
}
{
"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"
}
}
}
{
"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"
}
}
}
}
오브젝트와 레퍼런스 형태가 아닌 실제 값을 저장하는 자료형입니다.
프리미티브 형의 내장 함수를 사용 가능한것은 자바스크립트 처리 방식 덕분
let name = 'mark';
name.toString();
값자체가 변하지 않는 값을 의미합니다.
상수와 다른 것은 상수는 가리키는 포인터가 고정이라는 것이고, 리터럴은 그 자체가 값이자 그릇입니다.
35; // number 리터럴
'mark' // string 리터럴
true // boolean 리터럴
// object 리터럴
{
name: 'mark',
age: 35
}
값자체가 변하지 않는 값을 의미합니다.
상수와 다른 것은 상수는 가리키는 포인터가 고정이라는 것이고, 리터럴은 그 자체가 값이자 그릇입니다.
"리터럴 상수는 5, 1.23 과 같은 숫자나, 과 같은 문자열 등을 말합니다. 'This is a string' 혹은 "It’s a string!"이것들이 리터럴 상수라고 불리우는 이유는 이것들이 프로그램 내에 직접 문자 형태로(literally)지정되는 값들이기 때문입니다. 이러한 값들은 한번 지정되면 변하지 않습니다. 예를 들면 숫자2 는 언제나 자기 자신이 2라는 숫자임을 나타내며 어떤 다른 의미도 갖지 않습니다. 이들은 한번 지정되면 그 값을 변경할 수 없기 때문에 _상수_입니다. 그 중에서도 특별히 이러한 값들을 리터럴 상수라고 부릅니다." @kangpual
가장 기본적인 데이터 타입
단순한 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);
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` 형식을 사용합니다.
JavaScript 와 마찬가지로, TypeScript는 문자열 데이터를 둘러싸기 위해 큰 따옴표 ( " ) 나, 작은 따옴표 ( ' ) 를 사용합니다.
let name: string = "mark";
name = 'anna';
행에 걸쳐 있거나, 표현식을 넣을 수 있는 문자열
이 문자열은 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.";
TypeScript 에서 'undefined' 와 'null' 은 실제로 각각 'undefined' 와 'null' 이라는 고유한 타입을가집니다.
'void' 와 마찬가지로, undefined 와 null 은 그 자체로는 쓸모가 없습니다.
둘다 소문자만 존재합니다.
// 이 변수들에 할당할 수 있는 것들은 거의 없다.
let u: undefined = undefined;
let n: null = null;
기본 설정이 그렇습니다.
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 이라는 값으로 할당된 것을 null 이라고 합니다.
무언가가 있는데, 사용할 준비가 덜 된 상태.
null 이라는 타입은 null 이라는 값만 가질 수 있습니다.
런타임에서 typeof 연산자를 이용해서 알아내면, object 입니다.
let n: null = null;
console.log(n); // null
console.log(typeof n); // object
값을 할당하지 않은 변수는 undefined 라는 값을 가집니다.
무언가가 아예 준비가 안된 상태
object 의 property 가 없을 때도 undefined 입니다.
런타임에서 typeof 연산자를 이용해서 알아내면, undefined 입니다.
let u: undefined = undefined;
console.log(u); // undefined
console.log(typeof u); // undefined
타입이 없는 상태입니다.
`any` 와 반대의 의미를 가집니다.
Void 는 없습니다. 소문자입니다.
주로 함수의 리턴이 없을 때 사용합니다. 그 외에는 사용할 일이 거의 없습니다.
function returnVoid(message): void {
console.log(message);
}
returnVoid('리턴이 없다');
어떤 타입이어도 상관없는 타입입니다.
이걸 최대한 쓰지 않는게 핵심입니다.
왜냐면 컴파일 타임에 타입 체크가 정상적으로 이뤄지지 않기 때문입니다.
그래서 컴파일 옵션 중에는 any 를 쓰면 오류를 뱉도록 하는 옵션도 있습니다.
noImplicitAny
function returnAny(message): any {
console.log(message);
}
returnVoid('리턴은 아무거나');
리턴에 주로 사용됩니다.
아래 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<타입>
타입[]
let list: number[] = [1, 2, 3];
let list: Array<number> = [1, 2, 3];
배열인데 타입이 한가지가 아닌 경우
마찬가지로 객체입니다.
꺼내 사용할때 주의가 필요합니다.
배열을 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;
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];
ECMAScript 2015 의 Symbol 입니다.
프리미티브 타입의 값을 담아서 사용합니다.
고유하고 수정불가능한 값으로 만들어줍니다.
그래서 주로 접근을 제어하는데 쓰는 경우가 많았습니다.
let sym = Symbol();
let obj = {
[sym]: "value"
};
console.log(obj[sym]); // "value"
console.log(hoisted_var);
var hoisted_var = '변수를 아래서 선언했는데 사용이 위에서 가능';
////////////////////////////////////////
console.log(hoisted_let);
let hoisted_let = '변수를 아래서 선언했는데 사용이 위에서 불가';
var redeclare_var: string = '한번 선언 했는데';
var redeclare_var: string = '또 선언이 가능';
// var redeclare_var: number = 0; (X)
////////////////////////////////////////
let redeclare_let = '한번 선언 했기 때문에';
let redeclare_let = '또 선언이 불가';
/*
그렇지만 var 에서 재선언 하더라도 같은 타입이어야 함.
*/
let a: string = '에이';
let b = '비이';
const c: string = '씨이';
const d = '디이';
/*
1. a 는 명시적으로 지정된 타입인 string
2. b 는 타입추론에 의한 타입인 string
3. c 는 명시적으로 지정된 타입인 string
4. d 는 타입추론에 의한 타입인 리터럴타입 "디이"
*/
let someValue: any = "this is a string";
let strLength: number = (<string>someValue).length;
let strLength: number = (someValue as string).length;
/*
1. 주로 넓은 타입에서 좁은 타입으로 강제하는 경우가 많다.
2. jsx 에서는 as 를 쓴다.
*/
인터페이스랑 비슷해 보입니다.
Primitive, Union Type, Tuple
기타 직접 작성해야하는 타입을 다른 이름을 지정할 수 있습니다.
만들어진 타입의 refer 로 사용하는 것이지 타입을 만드는것은 아닙니다.
type MyStringType = string;
const str = 'world';
let myStr: MyStringType = 'hello';
myStr = str;
/*
별 의미가 없다..
*/
let person: string | number = 0;
person = 'Mark';
type StringOrNumber = string | number;
let another: StringOrNumber = 0;
another = 'Anna';
/*
1. 유니온 타입은 A 도 가능하고 B 도 가능한 타입
2. 길게 쓰는걸 짧게
*/
let person: [string, number] = ['Mark', 35];
type PersonTuple = [string, number];
let another: PersonTuple = ['Anna', 24];
/*
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 로
*/
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 처럼 동작한다.
*/
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 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 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 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(); // 안녕하세요! 입니다.
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 Person {
name: string;
age?: number;
}
interface Korean extends Person {
city: string;
}
const k: Korean = {
name: '이웅재',
city: '서울'
};
interface HelloPerson {
// (name: string, age: number): void;
(name: string, age?: number): void;
}
let helloPerson: HelloPerson = function (name: string) {
console.log(`안녕하세요! ${name} 입니다.`);
};
helloPerson('Mark'); // 안녕하세요! Mark 입니다.
/*
함수의 타입 체크는 할당할때가 아니라 사용할때 한다는 점을 명심
*/
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 = '백';
interface StringDictionary {
[index: string]: string;
name: string;
}
const sd: StringDictionary = {
name: '이름' // 필수
};
sd.any = 'any'; // 어떤 프로퍼티도 가능
////////////////////////////////////////////////
interface StringDictionaryNo {
[index: string]: string;
// name: number; // (X) 인덱서블 타입이 string 값을 가지기 때문에 number 를 필수로 끌어오면 에러
}
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 이 디폴트 이다.
*/
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}
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. 생성자가 불리기 전에 이미 프로퍼티의 값이 저장되어 있음을 알 수 있다.
*/
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 이 붙은 변수나 함수는 _ 를 이름앞에 붙이는데,
이는 문법이 아니라 널리 쓰이는 코딩 컨벤션이다.
*/
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 을 이용하면 클래스 외부애서 접근 불가
*/
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. 자식 클래스에서 디폴트 생성자는 부모의 생성자와 입력 형태가 같다.
*/
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 을 출력하는 것에 주의한다.
*/
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 를 함수처럼 설정하면, 추가 작업을 하고 셋팅할 수 있다.
*/
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 를 붙히면 똑같이 동작한다.
*/
class Person {
public static Talk(): void {
console.log('안녕하세요.');
}
}
Person.Talk(); // 안녕하세요.
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 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 Preference {
private constructor() {
}
}
// const p: Preference = new Preference(); (X)
/*
1. 생성자 함수 앞에 접근제어자인 private 을 붙일 수 있다.
2. 외부에서 생성이 불가능하다.
*/
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 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 만 있는 경우와 같다.
*/
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);
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());
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);
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 을 사용하면 정상적으로 사용가능
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 를 확인
*/
function hello<T>(messages: T[]): T {
return messages[0];
}
console.log(hello<string>(['Hello', 'World']));
/*
hello 함수의 제네릭 타입을 [] 를 이용하여 배열로 사용할 수 있음
*/
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 시 오류가 나지 않는다?
*/
class Person<T> {
private _name: T;
private _age: number;
constructor(name: T) {
this._name = name;
}
}
new Person('Mark');
// new Person<string>(35);
/*
명시적으로 제네릭 타입을 설정하면 오류
*/
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 은 안된다.
*/
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);
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);
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);
}
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
*/
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>;
}
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);
}
experimentalDecorators 추가
step4. vscode 컴파일 설정
${workspaceRoot}/node_modules/.bin/tsc
command + shift + <B>
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 는 팩토리 스타일
*/
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();
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();
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
*/
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
*/
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);
}
}
By Woongjae Lee
양재동 코드랩 타입스크립트 기초 강좌 20170902 / 20170909
Daangn - Frontend Core Team ex) NHN Dooray - Frontend Team Leader ex) ProtoPie - Studio Team