타입스크립트는 .ts 확장자를 가지며
작성 후 타입스크립트 컴파일러를 통해
자바스크립트 파일로 컴파일하여 사용
$ tsc sample.ts
# compiled to `sample.js`
크로스 플랫폼 지원
객체 지향 언어
정적 타입
DOM 제어
최신 ECMAScript기능 지원
$ npm install -g typescript
$ tsc --version
$ tsc ./src/index.ts
단일프로젝트에서만 사용하길 희망하는 경우
일반 지역 설치 후 npx tsc 명령으로 실행
$ npm install -D typescript
$ npx tsc --version
$ npx tsc ./src/index.ts
$ tsc ./src/index.ts --watch --strict true --target ES6 --lib ES2015,DOM --module CommonJS
tsconfig.json 파일로 옵션 관리
{
"compilerOptions": {
"strict": true,
"target": "ES6",
"lib": ["ES2015", "DOM"],
"module": "CommonJS"
},
"include": [
"src/**/*.ts"
],
"exclude": [
"node_modules"
]
}
repl.it
https://replit.com/
Parcel
$ mkdir typescript-test
$ cd typescript-test
$ npm init -y
$ npm install -D typescript parcel-bundler
TS Node
nodeJS 환경에서 테스트
$ mkdir typescript-test
$ cd typescript-test
$ npm init -y
$ npm install -D typescript @types/node ts-node
true, false 값
모든 부동 소수점 값
문자열 '(작은 따옴표) "(큰 따옴표)
// 문자열만 가지는 배열
let fruits: string[] = ['Apple', 'Banana', 'Mango'];
// Or
let fruits: Array<string> = ['Apple', 'Banana', 'Mango'];
// 숫자만 가지는 배열
let oneToSeven: number[] = [1, 2, 3, 4, 5, 6, 7];
// Or
let oneToSeven: Array<number> = [1, 2, 3, 4, 5, 6, 7];
let array: (string | number)[] = ['Apple', 1, 2, 'Banana', 'Mango', 3];
// Or
let array: Array<string | number> = ['Apple', 1, 2, 'Banana', 'Mango', 3];
값을 단언할 수 없다면 사용
let someArr: any[] = [0, 1, {}, [], 'str', false];
interface IUser {
name: string,
age: number,
isValid: boolean
}
let userArr: IUser[] = [
{
name: 'Neo',
age: 85,
isValid: true
},
{
name: 'Lewis',
age: 52,
isValid: false
},
{
name: 'Evan',
age: 36,
isValid: true
}
];
정해진 타입 고정된 길이 배열 표현
let tuple: [string, number];
tuple = ['a', 1];
tuple = ['a', 1, 2]; // Error - TS2322
tuple = [1, 'a']; // Error - TS2322
숫자 혹은 문자 값 집합에 이름을 부여할 수 있는 타입
값이 일정한 범위로 정해져 있는 경우에 유용
enum Week {
Sun,
Mon,
Tue,
Wed,
Thu,
Fri,
Sat
}
enum Week {
// ...
}
console.log(Week);
console.log(Week.Sun); // 0
console.log(Week['Sun']); // 0
console.log(Week[0]); // 'Sun'
모든 타입
let any: any = 123;
any = 'Hello world';
any = {};
any = null;
알 수 없는 타입
let a: any = 123;
let u: unknown = 123;
let v1: boolean = a; // 모든 타입(any)은 어디든 할당할 수 있습니다.
let v2: number = u; // 알 수 없는 타입(unknown)은 모든 타입(any)을 제외한 다른 타입에 할당할 수 없습니다.
let v3: any = u; // OK!
let v4: number = u as number; // 타입을 단언하면 할당할 수 있습니다.
typeof 연산자가 object로 반환하는 모든 타입
let obj: object = {};
let arr: object = [];
let func: object = function () {};
let nullValue: object = null;
let date: object = new Date();
// ...
모든 타입의 하위타입
let num: number = undefined;
let str: string = null;
let obj: { a: 1, b: false } = undefined;
let arr: any[] = null;
let und: undefined = null;
let nul: null = undefined;
let voi: void = null;
// ...
값을 반환하지 않는 함수에서 사용
function hello(msg: string): void {
console.log(`Hello ${msg}`);
}
절대 발생하지 않을 값
어떤 타입도 적용할 수 없음
function error(message: string): never {
throw new Error(message);
}
2개 이상의 타입을 허용
let union: (string | number);
union = 'Hello type!';
union = 123;
union = false; // Error - TS2322: Type 'false' is not assignable to type 'string | number'.
2개 이상의 타입 조합
// 기존 타입들이 조합 가능하다면 인터섹션을 활용할 수 있습니다.
interface IUser {
name: string,
age: number
}
interface IValidation {
isValid: boolean
}
const heropy: IUser = {
name: 'Heropy',
age: 36,
isValid: true // Error - TS2322: Type '{ name: string; age: number; isValid: boolean; }' is not assignable to type 'IUser'.
};
const neo: IUser & IValidation = {
name: 'Neo',
age: 85,
isValid: true
};
// 혹은 기존 타입(IUser, IValidation)과 비슷하지만, 정확히 일치하는 타입이 없다면 새로운 타입을 생성해야 합니다.
interface IUserNew {
name: string,
age: number,
isValid: boolean
}
const evan: IUserNew = {
name: 'Evan',
age: 36,
isValid: false
};
// myFunc는 2개의 숫자 타입 인수를 가지고, 숫자 타입을 반환하는 함수.
let myFunc: (arg1: number, arg2: number) => number;
myFunc = function (x, y) {
return x + y;
};
// 인수가 없고, 반환도 없는 경우.
let yourFunc: () => void;
yourFunc = function () {
console.log('Hello world~');
};
명시적으로 타입이 선언되어있지 않은 경우
타입스크립트가 타입 추론
let num = 12;
num = 'Hello type!'; // TS2322: Type '"Hello type!"' is not assignable to type 'number'.
타입스크립트가 타입추론을 통해 판단할 수 있는 범주를 넘는 경우
더이상 추론하지 않도록 지시 (추론하지 말라고 딱 잘라 말함)
function someFunc(val: string | number, isNumber: boolean) {
// some logics
if (isNumber) {
val.toFixed(2); // Error - TS2339: ... Property 'toFixed' does not exist on type 'string'.
}
}
// Error - TS2533: Object is possibly 'null' or 'undefined'.
function fnA(x: number | null | undefined) {
return x.toFixed(2);
}
// if statement
function fnD(x: number | null | undefined) {
if (x) {
return x.toFixed(2);
}
}
// Type assertion
function fnB(x: number | null | undefined) {
return (x as number).toFixed(2);
}
function fnC(x: number | null | undefined) {
return (<number>x).toFixed(2);
}
// Non-null assertion operator
function fnE(x: number | null | undefined) {
return x!.toFixed(2);
}
타입스크립트가 추론 가능한 특정 범위 내에서 타입 보장
function someFunc(val: string | number, isNumber: boolean) {
if (isNumber) {
(val as number).toFixed(2);
isNaN(val as number);
} else {
(val as string).split('');
(val as string).toUpperCase();
(val as string).length;
}
}
타입스크립트 여러 객체를 정의하는 일종의 규칙
interface IUser {
name: string,
age: number,
isAdult: boolean
}
let user1: IUser = {
name: 'Neo',
age: 123,
isAdult: true
};
// Error - TS2741: Property 'isAdult' is missing in type '{ name: string; age: number; }' but required in type 'IUser'.
let user2: IUser = {
name: 'Evan',
age: 456
};
interface IUser {
readonly name: string,
age: number
}
// 초기화
let user: IUser = {
name: 'Neo',
age: 36
};
user.age = 85; // Ok
user.name = 'Evan'; // Error - TS2540: Cannot assign to 'name' because it is a read-only property.
interface IName {
(PARAMETER: PARAM_TYPE): RETURN_TYPE // Call signature
}
interface IUser {
name: string,
getName(): string
}
class User implements IUser {
constructor(public name: string) {}
getName() {
return this.name;
}
}
const neo = new User('Neo');
neo.getName(); // Neo
interface IItem {
[itemIndex: number]: string // Index signature
}
let item: IItem = ['a', 'b', 'c']; // Indexable type
console.log(item[0]); // 'a' is string.
console.log(item[1]); // 'b' is string.
console.log(item['0']); // Error - TS7015: Element implicitly has an 'any' type because index expression is not of type 'number'.
interface IAnimal {
name: string
}
interface ICat extends IAnimal {
meow(): string
}
class Cat implements ICat { // Error - TS2420: Class 'Cat' incorrectly implements interface 'ICat'. Property 'name' is missing in type 'Cat' but required in type 'ICat'.
meow() {
return 'MEOW~'
}
}
type 키워드를 사용해 새로운 타입 조합을 만들 수 있다
type MyType = string;
type YourType = string | number | boolean;
type TUser = {
name: string,
age: number,
isValid: boolean
} | [string, number, boolean];
let userA: TUser = {
name: 'Neo',
age: 85,
isValid: true
};
let userB: TUser = ['Evan', 36, false];
function someFunc(arg: MyType): YourType {
switch (arg) {
case 's':
return arg.toString(); // string
case 'n':
return parseInt(arg); // number
default:
return true; // boolean
}
}
재사용을 목적으로 함수나 클래스의 선언 시점이 아닌
사용 시점에 타입을 선언할 수 있는 방법 제공
function toArray(a: number, b: number): number[] {
return [a, b];
}
toArray(1, 2);
toArray('1', '2'); // Error - TS2345: Argument of type '"1"' is not assignable to parameter of type 'number'.
제약 조건이 없어 모든 타입이 허용되는 제네릭
interface MyType<T> {
name: string,
value: T
}
const dataA: MyType<string> = {
name: 'Data A',
value: 'Hello world'
};
const dataB: MyType<number> = {
name: 'Data B',
value: 1234
};
const dataC: MyType<boolean> = {
name: 'Data C',
value: true
};
const dataD: MyType<number[]> = {
name: 'Data D',
value: [1, 2, 3, 4]
};
타입 구현 영역에서 사용하는 extends는 삼항 연산자를 사용할 수 있음
T extends U ? X : Y
type U = string | number | boolean;
// type 식별자 = 타입 구현
type MyType<T> = T extends U ? string : never;
// interface 식별자 { 타입 구현 }
interface IUser<T> {
name: string,
age: T extends U ? number : never
}
class Cat {
constructor(private name: string) {}
getName = () => {
console.log(this.name);
}
}
const cat = new Cat('Lucy');
cat.getName(); // Lucy
const getName = cat.getName;
getName(); // Lucy
function someFn(cb: any) {
cb();
}
someFn(cat.getName); // Lucy
function add(a: string, b: string): string; // 함수 선언
function add(a: number, b: number): number; // 함수 선언
function add(a: any, b: any): any { // 함수 구현
return a + b;
}
add('hello ', 'world~');
add(1, 2);
add('hello ', 2); // Error - No overload matches this call.
생성자 메소드와 일반 메소드 멤버와는 다르게
속성은 name:string 같이 (class body)에 별도로 타입 선언
class Animal {
name: string;
constructor(name: string) {
this.name = name;
}
}
class Cat extends Animal {
getName(): string {
return `Cat name is ${this.name}.`;
}
}
let cat: Cat;
cat = new Cat('Lucy');
console.log(cat.getName()); // Cat name is Lucy.
// Abstract Class
abstract class Animal {
abstract name: string; // 파생된 클래스에서 구현해야 합니다.
abstract getName(): string; // 파생된 클래스에서 구현해야 합니다.
}
class Cat extends Animal {
constructor(public name: string) {
super();
}
getName() {
return this.name;
}
}
new Animal(); // Error - TS2511: Cannot create an instance of an abstract class.
const cat = new Cat('Lucy');
console.log(cat.getName()); // Lucy
// Interface
interface IAnimal {
name: string;
getName(): string;
}
class Dog implements IAnimal {
constructor(public name: string) {}
getName() {
return this.name;
}
}
타입을 선언할 때 선택적 매개변수를 지정할 수 있음
function add(x: number, y?: number): number {
return x + (y || 0);
}
const sum = add(2);
console.log(sum);
interface IUser {
name: string,
age: number,
isAdult?: boolean
}
let user1: IUser = {
name: 'Neo',
age: 123,
isAdult: true
};
let user2: IUser = {
name: 'Evan',
age: 456
};
// Error - TS2532: Object is possibly 'undefined'.
function toString(str: string | undefined) {
return str.toString();
}
// Type Assertion
function toString(str: string | undefined) {
return (str as string).toString();
}
// Optional Chaining
function toString(str: string | undefined) {
return str?.toString();
}
const foo = null ?? 'Hello nullish.';
console.log(foo); // Hello nullish.
const bar = false ?? true;
console.log(bar); // false
const baz = 0 ?? 12;
console.log(baz); // 0
// myTypes.ts
// 인터페이스 내보내기
export interface IUser {
name: string,
age: number
}
// 타입 별칭 내보내기
export type MyType = string | number;
// 선언한 모듈(myTypes.ts) 가져오기
import { IUser, MyType } from './myTypes';
const user: IUser = {
name: 'HEROPY',
age: 85
};
const something: MyType = true; // Error - TS2322: Type 'true' is not assignable to type 'MyType'.
interface IUser {
name: string,
age: number
}
const userA: IUser = { // TS2741: Property 'age' is missing in type '{ name: string; }' but required in type 'IUser'.
name: 'A'
};
const userB: Partial<IUser> = {
name: 'B'
};
interface IUser {
name?: string,
age?: number
}
const userA: IUser = {
name: 'A'
};
const userB: Required<IUser> = { // TS2741: Property 'age' is missing in type '{ name: string; }' but required in type 'Required<IUser>'.
name: 'B'
};
interface IUser {
name: string,
age: number
}
const userA: IUser = {
name: 'A',
age: 12
};
userA.name = 'AA';
const userB: Readonly<IUser> = {
name: 'B',
age: 13
};
userB.name = 'BB'; // TS2540: Cannot assign to 'name' because it is a read-only property.
KEY를 속성으로 TYPE을
그 속성값의 타입으로 지정하는 새로운 타입 반환
type TName = 'neo' | 'lewis';
const developers: Record<TName, number> = {
neo: 12,
lewis: 13
};
TYPE에서 KEY로 속성을 선택한 새로운 타입 반환
interface IUser {
name: string,
age: number,
email: string,
isValid: boolean
}
type TKey = 'name' | 'email';
const user: Pick<IUser, TKey> = {
name: 'Neo',
email: 'thesecon@gmail.com',
age: 22 // TS2322: Type '{ name: string; email: string; age: number; }' is not assignable to type 'Pick<IUser, TKey>'.
};