typescript
Typescript
What is TypeScript
- 자바스크립트를 확장한 언어
- ES6가 표준(최신 ECMA 표준을 지원)
TypeScript Features
- 숫자 분리 기호, 초기화 및 클래스, 알 수 없는 타입 등과 같이 안전성과 가독성을 향상시키는 데 사용하지 쉽도록 최근 릴리즈됨
Improve Readablility with TypeScript Numeric Separators when working with Large Numbers
- 큰 숫자를 볼 때, 얼마나 큰 숫자인지 알기 힘들다. TypeScript를 사용하면 JavaScript를 변경하지 않고 숫자 분리 기호를 사용하여 사람이 읽을 수 있는 형식인 1_800_000같은 형식으로 숫자를 사용할 수 있다.(>=TypeScript 2.7)
class AmountInput {
private static MAX_ALLOWED = 99_999_999;
amount: number = 0;
showTooltip() {
//show tooltip
setTimeout(() => {
//hide tooltip
}, 2500);
}
formatMillion() {
return this.amount / 1_000_000 + "M";
}
}
Make TypeScript Class Usage Safer with Strict Property Initialization
tsconfig.json파일의 플래그strictPropertyInitialization: 속성을 초기화하지 않으면 TypeScript에서 에러를 throw함(오류를 throw 시킬 것인가에 대한 속성)
class Library {
titles: string[];
// titles: string[] | undefined;
constructor(){}
}
const library = new Library();
const shortTitles = library.titles.filter( // error("Cannot read property filter of undefined.") -> if(library.titles) {...}로 묶어주면 된다.
title => title.length < 5;
// 실행: `node index.js`
strictNullCheck: 컴파일시 type 문제에 대하여 경고
{
"compilerOptions": {
"strictPropertyInitialization": true,
"strictNullChecks": true
}
}
위의 방법은 Class property에서 초기화하는 방법이고, 아래의 내용은 생성자(constructor)에서 초기화하는 방법이다.class Library {
titles: string[]
constructor() { // 초기화하는 과정에서 Class property가 할당되기 때문에 오류가 없다.
this.titles = ["What if?", "Flow"];// this
}
}
- exclamation mark(!)
class Library {
titles!: string[] // 속성이 초기화될 것이라고 알려줌(런타임시)
constructor() {}
}
Use the JavaScript “in” operator for automatic type interface in TypeScript
in 연산자를 사용하여 객체에 특정 속성이 있는지 확인interface Admin {
id: string;
role: string:
}
interface User {
email: string;
}
function redirect(usr: Admin | User) {
if((<Admin>usr).role !== undefined) { // error(parameter가 Admin임을 확신할 수 없기 때문에 오류 발생)
routeToAdminPage(usr.role);
} else {
routeToHomePage(usr.email);
}
}
따라서 in 연산자를 사용하면 아래와 같다....
function redirect(usr: Admin | User) {
if("role" in usr) {
routeToAdminPage(usr.role);
} else {
routeToHomePage(usr.email);
}
}
Redux and readonly
- Redux: 액션과 상태를 기억(모든 액션이 하나의 type을 가져야 함)
- readonly: 읽기만 가능한 property 제한자. property를 사용할 수 있는 const. 값 변경 불가
Create Explicit and Readable Type Declarations with TypeScript mapped Type Modifiers
readonly 속성
const pet:IPet = {name: 'Buttons', age: 5};
const readonlyPet: ReadonlyPet = {
name: 'Buttons',
age: 5
}
pet.age = 6;
readonlyPet.age = 6; // error(readonly 속성은 값을 변경할 수 없음)
? 속성
type ReadonlyPet = {
readonly [K in key of IPet]?: IPet[K]; // 모든 속성을 선택 사항으로 표시하는 경우
}
type ReadonlyPet = {
readonly [K in key of IPet]: IPet[K]; // 기존 유형에 새로운 항목만 추가헐 수 있음
}
- readonly 속성 추가
interface IPet {
name: string;
age: number;
favoritePark?: string; //this
}
- 모든 속성 제거
type ReadonlyPet = {
readonly [K in key of IPet] -?: IPet[K]; // ?앞에 - 기호 추가(TypeScript >=2.8)
}
type ReadonlyPet = {
readonly [K in keyof IPet]-?: IPet[K];
}
const pet:IPet = {name: 'Buttons', age: 5};
const readonlyPet: ReadonlyPet = { // error(새로운 객체에 대한 속성을 모르기 때문)
name: 'Buttons',
age: 5,
favoritePark: 'Central'
}
// 따라서 아래와 같이 수정한다.
type ReadonlyPet = {
+readonly [K in keyof IPet]-?: IPet[K]; // - 기호에 대한 유연성을 갖기 위해 + 기호도 추가됨
}
Use Types vs. Interface
type과 interface는 TypeScript에서 바꾸어 사용이 가능하다.- type aliases
- interface와 마찬가지로 확장 가능(클래스에 의해 구현되거나 확장될 수 있음)
- 동일한 속성과 메서드를 가지고 있는 interface와 type이라면 변수 대입이 가능하다.
interface IAnimal {
age: number;
eat(): void;
speak(): string;
}
type AnimalTypeAlias = {
age: number;
eat(): void;
speak(): string;
};
let animalInterface: IAnimal;
let animalTypeAlias: AnimalTypeAlias;
animalInterface = animalTypeAlias; // this
export {};
그러나, 속성 또는 메서드의 이름이 변경되면 에러가 난다.type Cat = IPet & IFeline;
interface IPet {
pose(): void;
}
interface IFeline {
nightvision: boolean;
}
let cat: Cat; // cat.pose()와 cat.nightvision 둘 다 접근 가능
인터페이스를 사용하여 구현하려면 완전히 새로운 인터페이스를 구현해야 한다.type Cat = IPet & IFeline;
interface ICat extends IPet, IFeline {...}
let cat: ICat; // cat.pose()와 cat.nightvision 둘 다 접근 가능
인터페이스와 클래스 모두 인터페이스와 type을 상속받을 수 있다.(extend와 implements의 차이)type Pet = {
pose(): void;
};
interface IFeline {
nightvision: boolean;
}
interface ICat extends IFeline, Pet {}
type Cat = IFeline & Pet;
class HouseCat implements IFeline, Pet {}
export {};
interface와 type의 차이점- 두 개의 interface 또는 두 개의 type: 두 개의 interface를 사용하면 속성들이 병합
- 서로 다른 interface와 type: interface와 type을 함께 사용하면 속성들이 중복되더라도 모두 사용 가능
interface Foo {
a: string;
}
interface Foo {
b: string;
}
let foo: Foo; // foo.a와 foo.b 모두 접근 가능
type Foo = {
a: string;
}
type Foo = { // error(message: Duplicate identifier 'Foo')
b: string;
}
let foo: Foo;
- jQuery를 가져오는 예제
import * as $ from "jquery";
$.fn.extend({
hideChildren: function() {
// ...
}
});
$("test").hideChildren(); // 함수를 사용하려고 하면 error(jQuery does not exist...)
// TypeScript가 인터페이스 선언을 같은 이름으로 합치려는 동작 때문에
위에서는 jQuery 인터페이스는 적당하지 않기 때문에 typings.d.ts에 추가한다.interface JQuery {
hideChildren(): JQuery;
}
Build self-referencing type aliases in TypeScript
TypeScript에서는 자체를 참조하는 형식을 선언할 수 있다.interface TreeNode<T> {
value: T;
left: TreeNode<T>;
right: TreeNode<T>;
}
Simplify iteration of custom data structures in TypeScript with iterators
// iterator.ts
interface Action {
type: string;
}
interface ListNode<T> {
value: T;
next: ListNode<T>;
prev: ListNode<T>;
}
class BackwardsActionIterator implements IterableIterator<Action> {
constructor(private _currentActionNode: ListNode<Action>) {}
[Symbol.iterator](): IterableIterator<Action> {
return this;
}
next(): IteratorResult<Action> {
const curr = this._currentActionNode;
if(!curr || !curr.value) {
return {value: null, done: true};
}
//1. move through each item in the list
this._currentActionNode = curr.prev;
//2. return each item
return {value: curr.value, done: false};
}
}
let action1 = { type: "LOGIN" };
let action2 = { type: "LOAD_POSTS" };
let action3 = { type: "DISPLAY_POSTS" };
let action4 = { type: "LOGOUT" };
let actionNode1: ListNode<Action> = {
prev: null,
next: null,
value: action1
};
let actionNode2: ListNode<Action> = {
prev: actionNode1,
next: null,
value: action2
};
actionNode1.next = actionNode2;
let actionNode3: ListNode<Action> = {
prev: actionNode2,
next: null,
value: action3
};
actionNode2.next = actionNode3;
let actionNode4: ListNode<Action> = {
prev: actionNode3,
next: null,
value: action4
};
actionNode3.next = actionNode4;
const backwardsActionsList = new BackwardsActionIterator(actionNode4);
for(let action of backwardsActionsList) {
console.log(action.type);
}
tsc iterator.ts --target es6 command line에서 실행시키면 action4부터 차례대로 출력되는 것을 확인할 수 있다.Use the TypeScript “unknown” type to avoid runtime errors
any type은 런타임 시 오류를 유발할 수 있다.unknown type은 런타임 시 오류를 방지할 수 있다.Infer the Return Type of a Generic Function Type Parameter
infer keyword를 통해서 배열 요소의 type이나 함수의 return type을 얻을 수 있다. 이를 사용하여 FnReturnType type을 만들면 일반 매개 변수로 전달된 함수의 return type을 얻을 수 있다.function generateId(seed: number) {
return seed + 5;
}
lookupEntity(generateId(10));
function lookupEntity(id: string) {...}
// failed compile...
function generateId(seed: number) {
return seed + 5;
}
type ReturnType<T> = T extends (...args: any[]) => R ? R : any;
// T가 변수 수를 가진 함수를 확장한다면, ReturnType을 추론하여 R에 저장하고, R을 반환하거나, T가 함수를 확장하지 않으면 그냥 임의의 값을 자리 표시 자로 반환한다.
// type ReturnType<T> = T extends (...args: infer K) => R ? R : any;
type Id = ReturnType<typeof generateId>;
lookupEntity(generateId(10));
function lookupEntity(id: string) {...}
Deeply mark all the properties of a type as read-only in TypeScript(#14)
아래의 코드는 ITodo 인터페이스의 속성이 IEmail 속성들을 가지고 있다.interface IEmail {
from: string;
to: string[];
body: string;
}
interface ITodo {
isCompleted: boolean;
text: string;
linkedEmail: IEmail;
}
interface IRootState {
userId: string;
showCompletedOnly: boolean;
todoTypes: string[];
todos: ITodo[];
iconGrid: string[][];
}
function rootReducer(action: any, state: IRootState): IRootState {
// case action 1...
// case action 2...
return state;
}
Redux에서는 state 변경이 불가능해야 한다.Dynamically initialize class properties using TypeScript decorators
데코레이터를 사용하여 클래스의 속성을 초기화하는 방법과 특정 URL에 GET 요청다양한 추상화를 만들기 위해 여러 데코레이터를 연결하는 방법을 살펴본다.
@decorrator- TypeScript에 있는 함수(생성자 함수!!)
- 전체 파일이 로드될 때 호출
- 실제 인스턴스는 나중에 생성될 수 있음
Object.defineProperty구문을 이용하여 클래스 속성 이름에 대한 getter 정의
// like this...
function GetTodos(target: any, name: string) {
const init = () => {
return fetch('https://jsonplaceholder.typicode.com/todos')
.then(response => response.json());
};
Object.defineProperty(target, name, {
get: function() {
return init();
}
});
}
decorator는 실제로 여러 개를 만들어 서로 겹쳐 놓을 수 있다.
댓글
댓글 쓰기