[TOC]
✔ number / string / boolean / array
let car: string = 'bmw';
let age: number = 30;
let isAdult: boolean = true;
let a: number[] = [1, 2, 3];
let a2: Array<number> = [1, 2, 3];
let week1: string[] = ['mon', 'tue', 'wed'];
let week2: Array<string> = ['mon', 'tue', 'wed'];
week1.push(3); // 문자열 배열에 숫자를 추가하려고 하므로 Error 발생
✔ tuple
let b:[string, number]; // 배열의 첫번째 요소는 string, 두번째 요소는 number
b = ['z', 1]; // 가능
// b = [1, 'z']; // 순서가 반대이므로 불가능
b[0].toLowerCase();
// b[1].toLowerCase(); // number 타입이므로 불가능
✔ void / never
void
: 함수에서 반환하는 것이 없는 경우
function sayHello(): void {
console.log('hello');
}
never
: 항상 에러를 반환하거나 영원히 끝나지 않는 함수의 타입
function showError():never {
throw new Error();
}
function infLoop():never {
while (true) {
// do something ..
}
}
✔ enum
- 특정 값만 입력하도록 강제하고 싶을 때
- 값들이 공통점이 있을 때
enum Os {
Window,
Ios,
Android
}
// Os.Window = 1, Os.Ios = 2, Os.Android = 3으로 할당되어 있음
// 수동으로 값을 주지 않으면 0부터 1씩 증가하며 할당됨
enum Os {
Window = 3,
Ios,
Android
}
// Window = 3, Ios = 4, Android = 5로 할당됨
enum Os {
Window = 3,
Ios = 10,
Android
}
// Window = 3, Ios = 10, Android = 11로 할당됨
컴파일 된 결과
Os 객체 생성
양방향 매핑
enum Os { Window = 3, Ios = 10, Android } console.log(Os['Ios']) // 10 console.log(Os[10]) // "Ios"
- 문자열도 입력가능
enum Os {
Window = 'win',
Ios = 'ios',
Android = 'and'
}
컴파일 된 결과
- 단방향 매핑 (숫자가 아니므로)
enum Os {
Window = 'win',
Ios = 'ios',
Android = 'and'
}
let myOs: Os;
// Window, Ios, Android만 입력가능
myOs = Os.Window;
✔ null / undefined
let a:null = null;
let b:undefined = undefined;
let user:object;
user = {
name: 'xx',
age: 30
}
console.log(user.name);
// object에는 특정 속성값에 대한 정보가 없으므로 에러가 발생함
// 이를 해결하기 위해 interface를 사용
interface User {
name: string;
age: number;
}
let user: User = {
name: 'xx',
age: 30
}
user.age = 10; // 정상 작동
console.log(user.name); // 에러가 발생하지 않음
✔ Optional Property
interface User {
name: string;
age: number;
gender: string; // 2. gender 선언 시
}
// 3. user에는 gender가 없으므로 에러 발생
let user: User = {
name: 'xx',
age: 30
}
user.gender = "male" // 1. 에러발생
interface User {
name: string;
age: number;
gender?: string; // "?"를 추가하여 선택적인 속성으로 변경
}
let user: User = {
name: 'xx',
age: 30
}
user.gender = "male" // 에러가 발생하지 않음
✔ ReadOnly Property
interface User {
name: string;
age: number;
gender?: string;
readonly birthYear: number; // read-only 설정
}
let user: User = {
name: 'xx',
age: 30,
birthYear: 2000,
}
user.birthYear = 1990; // read-only이므로 수정 불가능
✔ 문자열 인덱스 서명 추가
interface User {
name: string;
age: number;
gender?: string;
readonly birthYear: number;
// 모두 optional하게 하기 번거롭다
1?: string;
2?: string;
3?: string;
4?: string;
}
let user: User = {
name: 'xx',
age: 30,
birthYear: 2000,
}
interface User {
name: string;
age: number;
gender?: string;
readonly birthYear: number;
[grade: number]: string;
// number를 key로 하고, string을 value로 하는 속성을 여러 개 받을 수 있음
// 여기서, grade 대신 x 등 다른 문자 사용가능
}
let user: User = {
name: 'xx',
age: 30,
birthYear: 2000,
1: 'A',
2: 'B'
}
✔ 문자열 리터럴 타입
- 위의 예시에서 grade의 value가 될 수 있는 범위가 넓음
type Score = 'A' | 'B' | 'C' | 'F';
interface User {
name: string;
age: number;
gender?: string;
readonly birthYear: number;
[grade: number]: Score;
// Score 내의 값 외에는 작성할 수 없음
}
let user: User = {
name: 'xx',
age: 30,
birthYear: 2000,
1: 'A',
2: 'B'
}
✔ 인터페이스 함수 정의
interface Add {
(num1: number, num2: number): number;
}
const add : Add = function(x, y) {
return x + y;
}
interface IsAdult {
(age: number):boolean;
}
const a: IsAdult = (age) => {
return age > 19;
}
✔ 인터페이스 클래스 정의
- implements
interface Car {
color: string;
wheels: number;
start(): void;
}
class Bmw implements Car {
color;
constructor(c: string) {
this.color: c;
}
wheels = 4;
start(){
console.log('go !');
}
}
const b = new Bmw('green');
console.log(b)
/*
Bmw : {
"wheels": 4,
"color": "green"
}
*/
b.start(); // "go !"
- extends
interface Car {
color: string;
wheels: number;
start(): void;
}
interface Benz extends Car {
door: number;
stop(): void;
}
const benz: Benz = {
door: 5,
stop(){
console.log('stop')
},
color: 'black',
wheels: 4,
start(){}
}
interface Car {
color: string;
wheels: number;
start(): void;
}
interface Toy {
name: string;
}
interface ToyCar extends Car, Toy {
price: number;
}
확장은 여러 개의 인터페이스를 확장할 수 있음
// void: return 값이 없는 경우
function add(num1: number, num2: number): void {
console.log(num1 + num2);
}
// return 값이 boolean인 경우
function isAdult(age: number): boolean {
return age > 19;
}
- 선택적 매개변수
// 함수의 매개변수도 optional 설정 가능
function hello(name: string): string {
return `Hello, ${name || 'world'}`;
}
const result = hello(); // 매개변수가 없으므로 에러 발생
------------------------------------------------------------------
// ? 로 name을 선택적 매개변수로 받아 에러 해결
// 선택적 매개변수도 타입을 지켜야 함
function hello(name?: string): string {
return `Hello, ${name || 'world'}`;
}
const result = hello();
const result2 = hello('hyeon');
--------------------------------------------------------------------
// 매개 변수의 default값 설정
function hello(name="world"): string {
return `Hello, ${name}`;
}
const result = hello();
- 필수 매개변수와 선택적 매개변수의 순서
function hello(name: string, age?: number):string {
if (age !== undefined) {
return `Hello, ${name}. You are ${age}.`;
} else {
return `Hello, ${name}`;
}
}
console.log(hello('hyeon'));
필수 매개변수가 선택적 매개변수보다 앞에 와야 함.
-> 지켜지지 못하면, 에러발생
function hello(age: number|undefined, name: string):string {
if (age !== undefined) {
return `Hello, ${name}. You are ${age}.`;
} else {
return `Hello, ${name}`;
}
}
console.log(hello(undefined, 'hyeon'));
선택적 매개변수를 앞에 두고 싶다면 undefined 사용
- 나머지 매개변수
- 매개변수의 개수가 매번 바뀔 수 있음
// 전달받은 매개변수를 배열로 나타낼 수 있게 함
function add(...nums: number[]) {
return nums.reduce((result, num) => result + num, 0);
}
add(1, 2, 3); // 6
interface User {
name: string;
}
// User type의 Hyeon 객체
const Hyeon: User = {name: 'hyeon'}
// this의 타입 정하기
function showName(this: User){
console.log(this.name)
}
// bind를 이용해 showName의 this를 Hyeon 객체로 강제하고 있음
const a = showName.bind(Hyeon);
a(); // hyeon
- 매개 변수와 this가 함께 존재할 경우
interface User {
name: string;
}
const Hyeon: User = {name: 'hyeon'}
// this를 제일 앞에
function showName(this: User, age: number, gender: 'm'|'f'){
console.log(this.name, age, gender)
}
const a = showName.bind(Hyeon);
a(30, 'm'); // 전달된 매개변수는 this 다음부터 순서대로 적용
interface User {
name: string;
age: number;
}
// join: validate 역할을 하는 함수
// age의 타입에 따라 반환하는 타입이 달라짐
function join(name: string, age: number|string): User|string {
if (typeof age === "number") {
return {
name,
age,
};
} else {
return "나이는 숫자로 입력해주세요.";
}
}
// 에러 발생: 반환하는 타입에 대한 확신이 없음
const sam: User = join("Sam", 30);
const jane: string = join("Jane", "30");
이를 해결하기 위해서는 오버로드(overload)를 사용
오버로드
: 전달받은 매개변수의 개수나 타입에 따라 다른동작을 하도록 함- 동일한 함수지만, 매개변수의 개수나 타입에 따라 다르게 동작해야 하는 경우 사용
interface User {
name: string;
age: number;
}
function join(name: string, age: number): User; // age가 number일 때, User를 반환한다고 판단
function join(name: string, age: string): string; // age가 string일 때, string을 반환한다고 판단
function join(name: string, age: number|string): User|string {
if (typeof age === "number") {
return {
name,
age,
};
} else {
return "나이는 숫자로 입력해주세요.";
}
}
const sam: User = join("Sam", 30);
const jane: string = join("Jane", "30");
const userName1 = "seung"; // const: 변하지 않는 값 선언
let userName2 = "hyeon"; // let: 변할 수 있는 값 선언
userName2 = 3; // 에러발생: userName2는 string으로 정의되어 있음
3을 할당하기 위해서는
let userName2: string | number = "hyeon"; // 타입을 설정해주어야 함
- userName1과 같이 정해진 string 타입 값을 가진 것 = 문자열 리터럴 타입
type Job = "police" | "developer" | "teacher";
interface User {
name: string;
job: Job;
}
const user: User {
name: "hyeon",
job: "developer" // job 속성은 Job에서 선언한 문자열만 사용가능
}
interface HighSchoolStudent {
name: string;
grade: 1 | 2 | 3;
}
|
: or를 의미
interface Car {
name: "car";
color: string;
start(): void;
}
interface Mobile {
name: "mobile";
color: string;
call(): void;
}
function getGift(gift: Car | Mobile) {
console.log(gift.color); // 둘 다 color를 가지므로 에러 발생 X
gift.start(); // Car에만 start가 존재하므로 에러발생
}
// Car type
interface Car {
name: "car";
color: string;
start(): void;
}
// Moblie type
interface Mobile {
name: "mobile";
color: string;
call(): void;
}
function getGift(gift: Car | Mobile) {
console.log(gift.color);
if(gift.name === "car") {
gift.start();
} else {
gift.call();
}
}
두 interface에 동일한 속성(name)의 타입을 다르게 정의함으로써 interface를 구분할 수 있음
❗ 검사해야할 항목이 많은 경우 switch
를 사용하는 것이 가독성이 좋음
- 여러 타입을 합쳐서 사용
&
: and를 의미- 필요한 모든 기능을 가진 하나의 타입이 만들어짐
interface Car {
name: string;
start(): void;
}
interface Toy {
name: string;
color: string;
price: number;
}
// Toy와 Car의 모든 속성을 기입해야 함
const toyCar: Toy & Car = {
name: "타요",
start() {},
color: "red",
price: 1000,
};
class Car {
color: string; // member 변수는 미리 선언해야 함
constructor(color: string) {
this.color = color; // 에러발생: color속성이 Car에 존재하지 않음
}
start() {
console.log("start");
}
}
const bmw = new Car("red");
class Car {
// constructor(readonly color: string) {
constructor(public color: string) {
this.color = color;
}
start() {
console.log("start");
}
}
const bmw = new Car("red");
-
member 변수를 미리 선언하지 않는 방법
-
접근 제한자 ( Access modifier )
public
/private
/protected
public
: 자식 클래스나 클래스 인스턴스에서 모두 접근 가능
class Car { // public name: string = "car"; name: string = "car"; color: string; constructor(color: string) { this.color = color; } start() { console.log("start"); } } class Bmw extends Car { constructor(color: string) { super(color); // super를 선언하지 않을 경우, 에러 발생 } showName() { console.log(super.name); // super의 name. 즉, Car의 name이 public이기 때문에 // 자식클래스 내부에서 접근해도 사용 가능 } } const z4 = new Bmw("black");
private
: 해당 클래스 내부에서만 접근 가능private
혹은#
을 붙여 사용
class Car { private name: string = "car"; // name은 private이므로, Car클래스 내부에서만 사용 가능 color: string; constructor(color: string) { this.color = color; } start() { console.log("start"); console.log(this.name); } } class Bmw extends Car { constructor(color: string) { super(color); } showName() { console.log(super.name); // 에러발생 } } const z4 = new Bmw("black");
class Car { #name: string = "car"; // name은 private이므로, Car클래스 내부에서만 사용 가능 color: string; constructor(color: string) { this.color = color; } start() { console.log("start"); console.log(this.#name); } } class Bmw extends Car { constructor(color: string) { super(color); } showName() { console.log(super.#name); // 에러발생 } } const z4 = new Bmw("black");
protected
: 자식 클래스에서 접근 가능- 자식 클래스 내부에서는 참조할 수 있으나, 클래스 인스턴스에서는 참조 불가능
class Car { protected name: string = "car"; color: string; constructor(color: string) { this.color = color; } start() { console.log("start"); console.log(this.name); } } class Bmw extends Car { constructor(color: string) { super(color); } showName() { console.log(super.name); } } const z4 = new Bmw("black"); console.log(z4.name); // 에러발생
-
readonly
: 해당 변수를 수정할 수 없도록class Car { readonly name: string = "car"; color: string; constructor(color: string) { this.color = color; } start() { console.log("start"); } } class Bmw extends Car { constructor(color: string) { super(color); } showName() { console.log(super.name); } } const z4 = new Bmw("black"); z4.name = "zzzz4"; // 에러발생: read-only property를 수정할 수 없다
- name을 수정하고 싶은 경우
class Car { readonly name: string = "car"; color: string; constructor(color: string, name) { this.color = color; this.name = name; } start() { console.log("start"); } } class Bmw extends Car { constructor(color: string, name) { super(color, name); } showName() { console.log(super.name); } } const z4 = new Bmw("black", "zzzz4");
-
-
static
: 정적 member 변수를 만들 수 있음- class. 으로 접근 가능
class Car {
readonly name: string = "car";
color: string;
static wheels = 4;
constructor(color: string, name) {
this.color = color;
this.name = name;
}
start() {
console.log("start");
console.log(this.wheels); // 에러 발생
}
}
class Bmw extends Car {
constructor(color: string, name) {
super(color, name);
}
showName() {
console.log(super.name);
}
}
const z4 = new Bmw("black", "zzzz4");
console.log(z4.wheels); // 에러 발생
class Car {
readonly name: string = "car";
color: string;
static wheels = 4;
constructor(color: string, name) {
this.color = color;
this.name = name;
}
start() {
console.log("start");
console.log(Car.wheels);
}
}
class Bmw extends Car {
constructor(color: string, name) {
super(color, name);
}
showName() {
console.log(super.name);
}
}
const z4 = new Bmw("black", "zzzz4");
console.log(Car.wheels);
- 추상 class
- 추상화 : property나 method의 이름만 선언해주고 구체적인 것은 상속받는 쪽에서 구현하는 것
- 추상 class를 상속받아 만든 객체들이 동일한 이름의 method를 가지지만, 구체적인 기능은 다 다를 수 있음
abstract class Car {
color: string;
constructor(color: string) {
this.color = color;
}
start(){
console.log("start");
}
}
const car = new Car("red");
// 에러 발생:
// 추상 클래스는 new를 이용해 객체를 만들 수 없음
// 상속을 통해서만 가능
class Bmw extends Car {
constructor(color: string) {
super(color);
}
}
const z4 = new Bmw("black");
abstract class Car {
color: string;
constructor(color: string) {
this.color = color;
}
start(){
console.log("start");
}
abstract doSomething(): void;
}
// 에러발생: 추상클래스 내부의 추상메서드는 "반드시" 상속받은 클래스 내부에서 구체적인 구현을 해야 함
class Bmw extends Car {
constructor(color: string) {
super(color);
}
}
const z4 = new Bmw("black");
doSomething()의 구체적인 기능을 정의함으로써 에러 해결 가능
class Bmw extends Car { constructor(color: string) { super(color); } doSomething() { alert(3); } }
- class 나 함수, 인터페이스를 다양한 타입으로 재사용 가능
- 선언 시, 타입 파라미터만 작성하고 생성 시점에 사용하는 타입 결정
function getSize(arr: number[] | string[]): number {
return arr.length;
}
const arr1 = [1, 2, 3];
getSize(arr1); // 3
const arr2 = ["a", "b", "c"];
getSize(arr2);
// Generic
// <T>: T는 다른 문자를 사용해도 됨
function getSize<T>(arr: T[]): number {
return arr.length;
}
const arr1 = [1, 2, 3];
getSize<number>(arr1);
// getSize<number | string>(arr1);
const arr2 = ["a", "b", "c"];
getSize<string>(arr2);
interface Mobile<T> {
name: string;
price: number;
option: T;
}
// option 객체의 모습이 정해져 있다면,
// const m1: Mobile<{ color: string; coupon: boolean; }> = {
const m1: Mobile<object> = {
name: "s21",
price: 1000,
option: {
color: "red",
coupon: false,
},
}
const m2: Mobile<string> = {
name: "s20",
price: 900,
option: "good",
}
interface User {
name: string;
age: number;
}
interface Car {
name: string;
color: string;
}
interface Book {
price: number;
}
const user: User = { name: "a", age: 10 };
const car: Car = { name: "bmw", color: "red" };
const book: Book = { price: 3000 }; // 매개 변수 name이 없음
// name: string인 객체를 확장한 형태
// name에는 string만 올 수 있음
function showName<T extends {name: string}>(data: T): string {
return data.name;
}
showName(user);
showName(car);
showName(book); // name이 없으므로 에러 발생
-
https://www.typescriptlang.org/docs/handbook/utility-types.html
-
keyof
: interface의 key 값들을 유니온 형태로 받을 수 있음
interface User {
id: number;
name: string;
age: number;
gender: "m" | "f";
}
type UserKey = keyof User; // 'id' | 'name' | 'age' | 'gender'
const uk: UserKey = ""; // 에러발생
const uk: UserKey = "id"; // User 인터페이스의 키값중 하나를 입력
Partial<T>
: property를 모두 optional로 바꿔줌
interface User {
id: number;
name: string;
age: number;
gender: "m" | "f";
}
// User 인터페이스에서 존재하는 속성 중 필요한 속성만 사용할 수 있음
let admin: Partial<User> = {
id: 1,
name: "hyeon"
}
Required<t>
: 모든 property를 필수로 바꿔줌
interface User {
id: number;
name: string;
age?: number;
}
// Error: Required로 인해 age도 필수 property가 되기 때문
let admin: Required<User> = {
id: 1,
name: "hyeon"
// age: 30, // 작성함으로 인해 에러 제거
}
Readonly<T>
: 읽기 전용
interface User {
id: number;
name: string;
age?: number;
}
let admin: Readonly<User> = {
id: 1,
name: "hyeon"
}
admin.id = 4; // Error: 수정 불가능
Record<K, T>
K
: key /T
: type
interface Score {
"1": "A" | "B" | "C" | "D";
"2": "A" | "B" | "C" | "D";
"3": "A" | "B" | "C" | "D";
"4": "A" | "B" | "C" | "D";
}
const score: Score = {
1: "A",
2: "C",
3: "D",
4: "B",
}
// Record<K, T>
type Grade = "1" | "2" | "3" | "4";
type Score = "A" | "B" | "C" | "D";
const Record: Record<Grade, Score> = {
1: "A",
2: "C",
3: "D",
4: "B",
}
interface User {
id: number;
name: string;
age: number;
}
function isValid(user: User) {
const result: Record<keyof User, boolean> = {
id: user.id > 0,
name: user.name !== '',
age: user.age > 0
}
return result;
}
Pick<T, K>
: T 타입에서 K 속성만 골라서 사용
interface User {
id: number;
name: string;
age: number;
gender: "M" | "W";
}
// User에서 id, name만 골라서 사용
const admin = Pick<User, "id"|"name"> = {
id: 0,
name: "hyeon"
}
Omit<T, K>
: T 타입에서 K 속성만 생략하여 사용
interface User {
id: number;
name: string;
age: number;
gender: "M" | "W";
}
// User에서 age, gender를 제외하고 사용
const admin = Omit<User, "age"| "gender"> = {
id: 0,
name: "hyeon"
}
Exclude<T1, T2>
: T1에서 T2와 겹치는 타입을 제외하고 사용
type T1 = string | number;
type T2 = Exclude<T1, number>; // T1에서 number를 제외하고 사용하기 때문에 string이 됨
❗ Omit은 property를 제외하고 사용
❗ Exclude는 type을 제외하고 사용
NonNullable<Type>
: null과 undefined을 제외한 타입 생성
type T1 = string | null | undefined | void;
type T2 = NonNullable<T1>; // string | void