Typescript
- 타입 + 자바스크립트
- 브라우저에서 실행하려면 별도의 컴파일 과정 필요
- 장점
- 에러의 사전 방지
- 의도하지 않은 타입의 인자가 전달되었을 때 런타임(브라우저)이 아닌 개발 단계에서 오류를 탐지하여 수정할 수 있음
- 코드 가이드 및 자동 완성 용이
- VSCode에서 해당 변수의 타입에 맞는 메소드 자동 완성
- 에러의 사전 방지
- 타입이라는 개념은 크게 아래 5가지를 의미
- type alias
- interface
- class
- enum
- import 구문
JSDoc
- 소스코드 파일에 주석을 달기 위해 사용되는 마크업 언어
- 자바스크립트에서 타입스크립트처럼 코딩 가능
- typedef / property를 사용하여 타입에 대한 힌트 제공 가능
/**
* @typedef {object} Address
* @property {string} city
* @property {string} street
*/
/**
* @typedef {object} User
* @property {string} name
* @property {string} email
* @property {Address} address
*/
/**
* @returns {Promise<User>}
*/
const fetchUser = () => {
return fetch(USER_URL).then(response => response.data);
}
// @ts-check
/**
* @param {number} a 첫 번째 숫자
* @param {number} b 두 번째 숫자
*/
const sum = (a, b) => {
return a + b;
}
- ts-check
- tsconfig의 checkJs의 역할
타입스크립트 컴파일
# npm i -g typescript
# tsc index.ts // index.ts를 index.js로 컴파일
기본 타입
- 총 12가지가 있음
- Number, String, Boolean, Null, Undefined
- Object, Array
let arr: number[] = [1,2,3]; // 혹은 let arr: Array<number> = [1,2,3];
- Tuple
- 고정 길이 타입 배열
let arr: [string, number] = ['hi', 10];
- Enum
- 특정 값들의 집합
- Void
- null 또는 undefined 값만 가질 수 있음
- Never
- while(true)처럼 끝나지 않음
- Any
- 모든 가능한 타입을 의미
함수 타입
- 파라미터 + 리턴 값에 각각 타입 지정
- 리턴 값은 자동 타입 추론하기도 함
- 선언한 함수의 파라미터와 인자(타입, 개수)가 다를 경우 Error
타입 지정
- :을 이용하여 해당 변수(or 함수)에 타입 지정
const num: number = 123;
const str: string = 'this is string';
- optional 파라미터(?:)
- 속성을 선택적으로 사용할 수 있음
interface User { name: string; age?: number; }
- 읽기 전용 속성(readonly)
- 변경 불가능한 속성 앞에 readonly를 붙여 선언
interface User { readonly name: string; age?: number; }
- 읽기 전용 배열(ReadonlyArray)
- 읽기 전용 배열을 생성할 수 있음
const arr: ReadonlyArray<number> = [1, 2, 3, 4];
- 유니온 타입(|)
- 여러 개의 타입 지정 가능
const logMessage = (value: string | number) => { console.log(value); };
- 특정 타입으로 추론되므로 메소드 자동완성에 용이
- 함수 내에서 별도의 타입 가드를 해주지 않는 이상, 여러 개의 타입의 공통 속성에만 접근 가능
- 인터섹션 타입(&)
- 여러 타입을 하나로 합치는 연산자
type Value = string & number; // 가능하지 않은 타입 === Never type NewPerson = Developer & Person;
- 함수의 타입 지정
- 함수의 파라미터와 반환 값을 지정
interface SumFunc { (a: number, b: number): number; } const SumFunc: SumFunc = (a, b) => { return a + b; };
- 클래스의 타입 지정
- 인터페이스를 implements하여 타입 지정
interface CraftBeer { beerName: string; nameBeer(beer: string): void; } class myBeer implements CraftBeer { beerName: string = 'Baby Guinness'; nameBeer(b: string) { this.beerName = b; } constructor() {} }
인터페이스(interface)
- 객체나 함수의 스펙, 클래스, 배열에 타입을 지정하는 데 사용
- 타입 뿐만 아니라 객체의 프로퍼티도 정의 가능
- 인터페이스 내에 지정되지 않은 속성 사용 시 오류 발생
- 타입 추론 오류를 피하기 위해서 as 키워드를 사용하여 타입 단언
- extends 키워드를 사용하여 확장 가능
- 동적 속성을 지정할 수 있음
- [ ] 내에 props를 이름으로 한 동적 속성을 추가할 수 있음
interface User { name: string; age: number; [propName: string]: any; } const newUser: User = { name: 'newUser', age: 25, newCol: 'newCol', newCol2: 'newCol2', };
- 타입 별칭(type alias)
- 특정 타입이나 인터페이스를 참조할 수 있는 타입 변수
- interface vs type
- 인터페이스는 동일한 이름으로 재선언 가능
- 확장 방식이 다름
- 인터페이스는 extends, 타입은 상속 불가
- 타입 방식은 VSCode 상에서 프리뷰 기능을 통해 해당 타입 내의 속성 정보도 조회 가능
이넘(Enum)
- 특정 값들의 집합을 의미하는 자료형
enum Direction {
Up,
Doun,
Left,
Right,
}
- 초기값을 주지 않으면 숫자형 이넘으로 동작하며, 첫 번째 요소부터 0, 1, 2, ... 1씩 증가
- 초기값을 주면 해당 값부터 1씩 증가
- 런타임에 객체 형태로 존재
- keyof를 사용해야 하는 상황에서는 keyof typeof로 사용
- 종류
- 숫자형 이넘
- 문자형 이넘
- 이넘의 모든 요소를 전부 초기화해줘야 함
- 복합 이넘
- 숫자형 + 문자형
- 드롭다운이나 특정 예외 상황을 처리할 때 사용하면 좋음
- 리버스 매핑
- key를 이용하여 value 접근 + value를 이용하여 key 접근이 가능
- 숫자형 이넘만 가능
enum Day { Sunday, Monday, Tuesday, } const val = Day.Monday; // 1 const key = Day[val] // Monday
클래스
- 기존 생성자 함수 기반의 문법적 편리성을 향상한 개념
class Person {
name: string;
age: number;
constructor(name: string, age: number) {
this.name = name;
this.age = age;
}
}
- 클래스 내부에서 사용하는 프로퍼티는 반드시 클래스 상위에 타입을 선언해야 함
- 기본적으로 프로퍼티는 public으로 동작
- 클래스 내부에서만 접근 가능한 프로퍼티는 private 키워드를 추가
- 읽기 전용 프로퍼티는 readonly 키워드를 추가
- 추상 프로퍼티(메소드)는 abstract 키워드를 추가
- get, set 키워드를 사용하여 getter/setter 지정 가능
- 프로토타입
- 객체를 생성할 때 중복되는 프로퍼티에 대한 상속 구현 가능
- 부모 프로토타입의 프로퍼티를 참고하여 사용
- 활용 사례
- 빌트인 자바스크립트 API
- Array.prototype의 push, pop 등
- Object.prototype의 hasOwnProperty 등
- 빌트인 자바스크립트 API
- 객체를 생성할 때 중복되는 프로퍼티에 대한 상속 구현 가능
- 추상 클래스
- class 키워드 앞에 abstract 키워드를 붙여 선언 가능
abstract class StudentImpl { abstract studentNo : number; // 반드시 구현해야 함 patrt: string; // 구현 안해도 됨 abstract study(subject: string): void; sleep(): void; } class Student { studentNo: number; // 기본값이 있는 경우에는 =로 지정 constructor(studentNo: number, part: string) { this.studentNo = studentNo; } study(subject: string): void { console.log(`I'm studying ${subject}`); } }
제네릭
- 타입을 함수의 파라미터처럼 사용하는 것
- 중복되는 로직을 줄일 수 있음
const logTest = <T>(text: T): T => {
console.log(text);
return text;
};
logTest<number>(10);
logTest(10);
logTest<string>('hello world');
- 자동 타입 추론이 가능한 경우에는 <> 내에 타입을 전달하지 않아도 됨
- 타입을 전달받아 사용하므로 특정 타입에서만 제공되는 메소드를 사용하지 못하는 경우 발생
- 해결 방법
- 배열의 length 메소드를 사용하고자 할 경우에는 Array 자체를 제너릭으로 전달받는 것이 아닌 배열의 요소 타입을 전달받도록 코드 수정
const logText<T> = (text: T): T => { console.log(text.length); return text; }
- 해결 방법
- 제네릭 vs 유니온 타입(|)
- 유니온 타입은 해당 타입에서 공통적으로 갖고 있는 메소드만 함수 내에서 사용 가능
- 제네릭은 인자로 전달한 타입
- 인터페이스에 제네릭 적용
interface Item<T> { value: T; selected: boolean; } const emails: Array<Item<string>> = [ { value: 'naver.com', selected: true }, { value: 'gmail.com', selected: false }, { value: 'hanmail.net', selected: false }, ];
- 제네릭의 타입 제한
- length 메소드의 경우에는 제네릭에서 사용하려고 할 때 오류 발생
- 힌트 용도의 타입을 생성하여 extends 키워드로 타입 추론 힌트 전달
interface LengthType { length: number; } const logTextLength = <T extends LengthType>(text: T): number => { return text.length; };
- keyof를 이용하여 이미 선언된 타입의 요소만 허용 가능
interface ShoppingItem { name: string; price: number; stock: number; } // ShoppingItem의 키인 'name', 'price', 'stock'만 T에 전달 가능 const getShoppingItemOption = <T extends keyof ShoppingItem>(ItemOption: T): T => { return ItemOption; };
타입 추론
- 타입스크립트가 자동적으로 변수 선언 혹은 초기화 시 타입을 추론하는 것
- 기본적으로는 any, 파라미터의 기본 값이 있는 경우에는 해당 값의 타입으로 추론
- Duck Typing
- 객체의 변수 및 메소드의 집합이 객체의 타입을 결정
- Structural Subtyping
- 객체의 실제 구조나 정의에 따라 타입을 결정
타입 호환
- 변환할 타입에서 요구하는 속성을 모두 갖고 있는 경우에는 타입 호환 가능
- interface, type, class 키워드와는 상관 없음
interface Developer2 { name: string; skill: string; } interface Person2 { name: string; } let dev: Developer2; let p: Person2; dev = p; // 타입 호환 불가 p = dev; // 타입 호환 가능
- 이넘의 타입 호환
- 숫자형 이넘은 number 타입과 호환되지만 이넘끼리는 호환 X
- 클래스의 타입 호환
- static 프로퍼티와 constructor를 제외하고 비교
- 함수의 타입 호환
- 파라미터의 종류에 따라 타입 호환
let add = (a: number) => { return a; }; let sum = (a: number, b: number) => { return a + b; }; add = sum; // 타입 호환 불가 sum = add; // 타입 호환 가능
- 제네릭의 타입 호환
- 동적 타입 T가 할당된 후를 기준으로 비교
- 요소의 타입이나 종류가 다르면 호환 불가
타입 단언
- 개발자가 타입을 더 잘 알고 있는 경우, as 키워드를 사용하여 타입을 지정해주는 것
- 사용 예시
- DOM API를 조작할 때 사용
- querySelector 등의 DOM API를 사용하면 HTML 요소가 아닌 null이 반환될 수 있으므로 타입 단언을 이용하여 해당 요소의 메소드에 정상적으로 접근하도록 함
- 유니온 타입(|) 사용
- 유니온 타입의 공통적인 프로퍼티에만 접근 가능
- 타입 목록 중 하나의 타입으로 단언하여 해당 타입의 프로퍼티에 접근
- DOM API를 조작할 때 사용
타입 가드
- 타입 단언으로 일일이 예외처리하는 것이 아닌 타입을 추론하기 위한 별도의 함수를 정의하여 사용하는 것
interface Developer {
name: string;
skill: string;
}
interface Person {
name: string;
age: number;
}
const introduce = (): Developer | Person => {
return {
name: 'Tony',
age: 33,
skill: 'Iron Making',
};
};
const isDeveloper = (target: Developer | Person): target is Developer => {
return (target as Developer).skill !== undefined;
};
const tony = introduce();
const isTonyDev = isDeveloper(tony);
console.log(isTonyDev ? tony.skill : tony.age);
타입 모듈화
- ES6 Modules 개념과 유사
- 모듈 스코프, export / import 키워드 등
d.ts 파일
- 타입스크립트 코드의 타입 추론을 돕는 파일
tsconfig.json 파일
- 타입스크립트를 자바스크립트로 변환할 때의 설정을 정의하는 파일
- tsc 명령어
- 타입스크립트 → 자바스크립트 컴파일 명령어
- 속성
- files
- 컴파일할 개별 파일 지정
- include
- 컴파일할 폴더 경로 지정
- exclude
- 컴파일하지 않을 폴더 경로 지정
- 기본적으로 node_modules, bower_components 폴더 제외
- extends
- 다른 타입스크립트 설정 파일을 import하여 추가
- target
- 컴파일했을 때 생성되는 자바스크립트 결과물의 버전
- 기본값은 es3 / 그 외 es5, es6, esnext 등
- lib
- 컴파일할 때 포함될 라이브러리의 목록
- { "lib": ["es2015", "dom", "dom.iterable"] }
- allowJs
- 자바스크립트 파일도 함게 포함하여 컴파일할지에 대한 여부
- files
전역 변수 선언
- 맨 앞에 declare 키워드를 붙여 전역 변수/함수 선언
인덱싱
- 숫자로 인덱스에 접근하도록 인터페이스 선언
interface StringArray {
[index: number]: string;
}
const arr: StringArray = ['Thor', 'Hulk'];
arr[0]; // 'Thor'
- 배열 요소의 변경을 제한하기 위해서는 인덱스 요소 앞에 readonly 키워드 추가
유틸리티 타입(제네릭 타입)
Partial
- 특정 타입의 부분 집합을 만족하는 타입 정의
interface Address {
email: string;
address: string;
}
type MayHaveEmail = Partial<Address>;
const me: MayHaveEmail = {}; // 가능
const you: MayHaveEmail = { email: 'test@abc.com' }; // 가능
const all: MayHaveEmail = { email: 'capt@hero.
com', address: 'Pangyo' }; // 가능
Pick
- 특정 타입에서 몇 개의 속성을 선택하여 타입 정의
interface Product {
id: number;
name: string;
price: number;
brand: string;
stock: number;
something: object;
}
const displayProductDetail = (id: number): Pick<Product, 'id' | 'name' | 'price'> => {
// return ...;
};
Omit
- 특정 타입에서 지정된 속성만 제거한 타입 정의
interface AddressBook {
name: string;
phone: number;
address: string;
company: string;
}
const phoneBook: Omit<AddressBook, 'address'> = {
name: '재택근무',
phone: 12342223333,
company: '내 방'
}
const chingtao: Omit<AddressBook, 'address'|'company'> = {
name: '중국집',
phone: 44455557777
}
맵드 타입(Mapped Type)
기존에 정의된 타입을 새로운 타입으로 변환하는 문법
type Subset<T> = {
[K in keyof T]?: T[K];
}
interface Person {
age: number;
name: string;
}
const ageOnly: Subset<Person> = { age: 23 };
const nameOnly: Subset<Person> = { name: 'Tony' };
const ironman: Subset<Person> = { age: 23, name: 'Tony' };
const empty: Subset<Person> = {};
- 자바스크립트의 map 고차함수와 유사한 역할
'Language > Javascript' 카테고리의 다른 글
Javascript - 업로드한 이미지 미리보기 구현 (0) | 2022.04.20 |
---|---|
Javascript - navigator.userAgent이란? + 값 변경하는 법 (4) | 2022.04.11 |
Typescript - type alias vs interface 방식 비교 (2) | 2022.02.02 |
Javascript - Symbol(심볼)이란? (1) | 2021.12.30 |
Javascript - axios vs fetch vs ajax (0) | 2021.12.15 |