동그란 도그린
[타입스크립트] 한 입 크기로 잘라먹는 타입스크립트 정리 (1. 타입스크립트 개론 ~ 5. 인터페이스) 본문
1️⃣ 타입스크립트 개론
✨ 타입스크립트란?
기존의 자바스크립트를 더 안전하게 사용할 수 있도록 타입과 관련된 여러 기능(타입 지정 등)을 추가한 언어 (자바스크립트의 확장판) ⇒ 타입스크립트에서 자바스크립트 기본 문법들은 모두 사용 가능
✨ 타입스크립트 등장 배경
자바스크립트는 처음에 간결한 일부 프로그램을 만들기 위해 사용되었기 때문에 아래와 같은 특성을 가짐
- 유연한 문법
- 버그 발생 가능성 높음
- 자유로움
하지만 Node.js(자바스크립트 런타임)이 탄생하며 자바스크립트를 어디서든 실행할 수 있게 되면서 다양한 프로그램을 만드는 데 사용됨
⇒ 자바스크립트가 유연해서 프로그램을 안정성을 떨어뜨림!
⇒ 이를 극복하기 위해 안정성을 추가한 타입스크립트가 등장
❓ 타입 시스템이란?
프로그래밍 언어를 사용할 때 타입과 관련해서 지켜야 하는 규칙들을 모아둔 문법 체계
- 정적 타입 시스템 (코드 실행 전 모든 변수의 타입을 고정적으로 결정) - C, Java
- 동적 타입 시스템 (코드 실행 후 그때그때 변수의 타입을 결정, 현재 변수에 담긴 값에 따라 변수의 타입이 계속 달라짐) - Python, JavaScript
// 동적 타입 시스템의 단점 - 아래 코드가 실행됨 (오류는 발생)
let a = "hello";
a = 109283;
a.toUpperCase();
✨ 타입스크립트 특징
- 정적 타입 시스템처럼 코드 실행 전에 변수의 타입을 결정하고 타입 오류가 없는지 코드 검사
- 모든 변수에 일일이 직접 타입을 명시하지 않아도 됨 (변수에 담기는 초기값을 기준으로 타입을 자동으로 추론)
⇒ 점진적 타입 시스템 (Gradual Type System)
✨ 컴파일 과정
- 대다수의 프로그래밍 언어
코드를 AST 추상 문법 트리로 변환 → AST를 바이트 코드로 변환 → 컴파일 종료
- 타입스크립트
코드를 AST로 변환 → 타입 검사(타입 오류 검사) → 오류 있으면 종료 / 오류 없으면 AST를 자바스크립트 코드(타입 관련 문법은 삭제됨)로 변환 → 컴파일 종료
❗ tsc 파일명은 특정 파일을 컴파일하도록 타입스크립트 컴파일러에게 지시하는 명령어
✨ 컴파일러 옵션
- include 옵션
tsc에게 컴파일할 타입스크립트 파일의 범위와 위치를 알려주는 옵션 ⇒ 파일이 많을 때 일일이 tsc 명령어와 파일명을 입력하지 않아도 됨
- target 옵션
컴파일 결과 생성되는 자바스크립트 코드의 버전을 설정하는 옵션
- module 옵션
자바스크립트 코드의 모듈 시스템을 설정하는 옵션
- outDir 옵션
컴파일 결과 생성할 자바스크립트 코드의 위치 결정하는 옵션
- strict 옵션
타입스크립트 컴파일러의 타입 검사 엄격한 수준을 정하는 옵션
- ModuleDetection 옵션
모든 타입스크립트 파일에 export 키워드를 자동으로 추가하여 격리된 모듈로 취급되도록 하는 옵션
- ts-node 옵션
타입스크립트 모듈 실행 가능하게 하는 옵션
2️⃣ 타입스크립트 기본
✨ 타입스크립트 기본 타입
- 원시 타입
- 동시에 한 개의 값만 저장할 수 있는 타입들
- number, string, boolean 등
// number let num1: number = 123; let num2: number = Infinity; let num3: number = NaN; // string let str1: string = "hello"; let str2: string = `hello ${str1}`; // boolean let bool1: boolean = true; // null let null: null = null; // undefined let unde1: undefined = undefined;
- 리터럴 타입
- 하나의 값만 포함하도록 값 자체로 만들어진 타입
let strA: "hello" = "hello"; let boolA: true = true;
- 배열 타입
- 소괄호와 바를 이용해 다양한 타입의 배열 요소를 갖도록 정의 가능
let strArr: string[] = ["hello", "im", "winterload"]; let boolArr: boolean[] = [true, true, false]; // 여러 타입 let multiArr: (number | string)[] = [1, "hello"]; // 다차원 배열 let doubleArr: number[][] = [ [1, 2, 3], [4, 5], ];
- 튜플 타입 ⇒ 결국은 배열
- 자바스크립트에는 없는 타입스크립트의 특수 타입
- 길이와 타입이 고정된 배열
let tup1: [number, number] = [1, 2]; let tup2: [number, string, boolean] = [1, "hello", true];
- 객체 타입
- 객체 타입 정의 방법 2가지
- object로 정의 (⇒ 프로퍼티에 접근하려 하면 오류)
- 객체 리터럴 타입으로 정의
// object로 정의 let user: object = { id: 1, name: "이정환", }; // 객체 리터럴 let user: { id: number; name: string; } = { id: 1, name: "이정환", };
- 객체 타입 정의 방법 2가지
- 열거형 (Enum) 타입 ⇒ 컴파일될 때 다른 타입과 같이 사라지지 않고 자바스크립트 객체로 변환됨
- 자바스크립트에 존재하지 않고 타입스크립트에서만 사용 가능
- 여러개의 값을 나열하는 용도
enum Role { ADMIN, USER, GUEST, } // 숫자 할당도 가능 enum Role1 { ADMIN = 0, USER = 1, GUEST = 2, }
- any 타입 ⇒ 최대한 사용 X
- 타입 검사를 받지 않는 특수한 치트키 타입
- 모든 타입스크립트의 문법과 규칙으로부터 자유롭지만, 그만큼 위험한 타입
- Unknown 타입
- any보다 안전한 타입
- 어떤 타입의 값이든 다 저장 가능
- (반대는 성립 안됨) unknown 타입의 값은 어떤 타입의 변수에도 저장 불가
- 어떤 연산에도 참여 X, 어떤 메서드도 사용 X
let unknownVar: unknown; unknownVar = ""; unknownVar = 1; unknownVar = () => {}; let num: number = 10; num = unknownVar; // 오류 unknownVar * 2; // 오류
- void 타입
- 아무런 값도 없음을 의미하는 타입
- 보통 아무런 값도 반환하지 않는 함수의 반환값 타입을 정의할 때 사용
- 변수의 타입을 void로 정의하면 undefined 이외의 다른 타입의 값 저장 X
let a: void; a = undefined;
- never 타입
- 불가능을 의미하는 타입
- 함수가 어떤 값도 반환할 수 없는 상황일 때 해당 함수의 반환값 타입을 정의할 때 사용
- 변수를 never 타입으로 정의하면 any를 포함한 어떤 타입의 값도 이 변수에 저장 불가
function func3(): never { // 영원히 종료 불가 -> 값 반환 못함 -> never 타입 while(true) {}
❗ 타입 주석/타입 어노테이션 : 변수의 이름 뒤에 콜론(:)과 함께 변수의 타입을 정의하는 문법
❗ 옵셔널 프로퍼티 (?) : 생략 가능한 프로퍼티
❗ 읽기 전용 프로퍼티 (readonly)
let user: {
id?: number; // 옵셔널 프로퍼티 (생략 가능)
readonly name: string; // 읽기 전용 프로퍼티
} = {
id: 1,
name: "이정환",
};
✨ 타입 별칭 (Type Alias)
- 변수를 선언하듯이 타입을 별도로 정의 가능
- type 타입_이름 = 타입 형태로 타입 정의
type User { id: number; name:String }; let user: User = { id: 1, name: "이정환", };
✨ 인덱스 시그니처
- 객체 타입을 유연하게 정의할 수 있도록 돕는 특수 문법
- 인덱스 시그니처를 사용하면서 동시에 추가적인 프로퍼티를 또 정의할 때에는 인덱스 시그니처의 value 타입과 직접 추가한 프로퍼티의 value 타입이 호환되거나 일치해야 함!!
type CountryCodes = {
[key: string]: string;
};
let countryCodes: CountryCodes = {
Korea: "ko",
UnitedState: "us"
};
type CountryNumberCodes = {
[key:string]: number;
Korea: string; // 오류: 인덱스 시그니처의 value 타입과 호환되거나 일치해야
3️⃣ 타입스크립트 이해하기
✨ 초과 프로퍼티
타입에 정의된 프로퍼티 외의 다른 초과된 프로퍼티를 갖는 객체를 변수에 할당할 수 없도록 막음
⇒ 변수를 초기화할 때 객체 리터럴을 사용하지 않으면 발생하지 않음
type Book = {
name: string;
price: number;
};
type ProgrammingBook = {
name: string;
price: number;
skill: string;
};
// 아래 코드 오류 발생
let book2: Book = {
name: "한 입 크기로 잘라먹는 리액트",
price: 33000,
skill: "reactjs",
};
let book3: Book = programmingBook; // 오류 발생 X
✨ 대수 타입
여러 개의 타입을 합성해서 만든 타입
1. 합집합(Union) 타입
let a: string | number;
a = 1;
a = "hello";
type Dog = {
name: string;
color: string;
};
type Person = {
name: string;
language: string;
};
type Union1 = Dog | Person;
let union3: Union1 = {
name: "",
color: "",
language: "",
};
2. 교집합(Intersection) 타입
let variable: number & string; // 교집합 없는 서로소 집합이므로 never 타입으로 추론됨
✨ 타입 추론 가능한 경우
- 변수 선언
- 초기값을 기준으로 타입 추론
- 구조 분해 할당
- 객체와 배열을 구조 분해 할당하는 경우
- 함수의 반환 값
- return 문을 기준으로 추론
- 기본 값이 설정된 매개변수
- 기본 값 기준으로 추론
✨ 주의할 상황
- 암시적으로 any 타입으로 추론
- 변수 선언 시 초기 값을 생략하면 any 타입으로 추론됨 (매개변수의 타입이 any로 추론될 때와 달리 일반 변수의 타입이 any로 추론될 때는 오류로 판단되지 X)
- const 상수의 추론
- const로 선언된 상수도 타입 추론 진행됨
- 상수는 초기 값을 변경할 수 없으므로 좁은 타입으로 추론됨
✨ 타입 단언
- 값 as 타입으로 특정 값을 원하는 타입으로 단언 가능
- 다음 조건 만족 시 단언 가능 (A as B)
- A가 B의 슈퍼 타입일 때
- A가 B의 서브 타입일 때
- 다중 단언도 가능
- 다중 단언은 왼쪽 → 오른쪽으로 단언 이루어짐
type Person = {
name: string;
age: number;
};
let person = {} as Person; // Person 타입이라고 단언
person.name = "";
person.age = 23;
let num3 = 10 as unknown as string; // 다중 단언
✨ const 단언
변수를 const로 선언한 것과 비슷하게 타입 변경됨
let num4 = 10 as const;
let cat = {
name: "야옹이",
color: "yellow",
} as const;
✨ Non Null 단언
- 값 as 타입 형태를 따르지 않는 단언
- 값 뒤에 느낌표(!)를 붙여주면 이 값이 undefined이거나 null이 아닐 것을 단언할 수 있음
type Post = {
title: string;
author?: string;
};
let post: Post = {
title: "게시글1",
};
const len: number = post.author!.length; // Non Null 단언
✨ 타입 가드
- 조건문을 함께 사용해 타입을 좁히는 표현들
- instanceof 타입 가드 (Instanceof는 우리가 직접 만든 타입에는 사용 불가)
- in 타입 가드 (우리가 직접 만든 타입에 사용 가능)
✨ 서로소 유니온 타입
교집합이 없는(서로소 관계) 타입들을 모아 만든 유니온 타입
4️⃣ 함수와 타입
✨ 화살표 함수 타입 정의
const add = (a: number, b: number): number => a + b;
✨ 선택적 매개변수 설정
- 매개변수 이름 뒤에 물음표(?)를 붙이면 선택적 매개변수가 되어 생략 가능
- 선택적 매개변수는 필수 매개변수 앞에 올 수 없음, 반드시 뒤에 배치
function introduce(name="이정환", tall?: number) {
console.log(`name: ${name}`);
}
✨ 함수 타입 표현식
- 함수 타입을 타입 별칭과 함께 별도로 정의
- 함수 타입 표현식을 사용하면 함수 선언 및 구현 코드와 타입 선언을 분리 가능
type Operation = (a: number, b: number) => number;
const add: Operation = (a, b) => a + b;
const sub: Operation = (a, b) => a - b;
const multiply: Operation = (a, b) => a * b;
const divide: Operation = (a, b) => a / b;
// 타입 별칭 없이도 사용 가능
const add: (a: number, b: number) => number = (a, b) => a + b;
✨ 호출 시그니처
- 함수 타입 표현식과 동일하게 함수의 타입을 별도로 정의하는 방식
type Operation2 = {
(a: number, b: number): number;
name: string; // 호출 시그니처 아래에 프로퍼티를 추가 정의하는 것도 가능 => 하이브리드 타입
};
const add2: Operation2 = (a, b) => a + b;
const sub2: Operation2 = (a, b) => a - b;
const multiply2: Operation2 = (a, b) => a * b;
const divide2: Operation2 = (a, b) => a / b;
5️⃣ 인터페이스
✨ 인터페이스
- 타입 별칭과 동일하게 타입에 이름을 지어주는 문법
- 타입 별칭과 문법만 다를 뿐 기본적인 기능은 거의 같음
- 인터페이스에도 선택적 프로퍼티, 읽기 전용 프로퍼티 설정 가능
- Union과 Intersection 타입 정의 불가 ( 타입 별칭과 함께 사용하거나 타입 주석에서 직접 사용하는 건 가능)
interface Person {
name: string;
age?: number; // 선택적 프로퍼티
readonly job: string; // 읽기 전용 프로퍼티
sayHi: () => void; // 메서드 타입 정의
}
✨ 메서드 오버로딩
- 함수 타입 표현식으로 메서드 타입을 정의하면 메서드 오버로딩 불가
- 호출 시그니처로 메서드 타입을 정의하면 오버로딩 가능
interface Person {
readonly name: string;
age?: number;
sayHi: () => void; // 함수 표현식으로 메서드 타입 정의
sayHi: (a: number, b: number) => void; // 오버로딩 불가
}
interface Person {
readonly name: string;
age?: number;
sayHi(): void;
sayHi(a: number): void; // 호출 시그니처로 메서드 타입 정의
sayHi(a: number, b: number): void; // 오버로딩 가능
}
✨ 인터페이스 확장
- extends 를 통해 인터페이스 확장 가능
interface Animal {
name: string;
color: string;
}
interface Dog extends Animal { // name, color 프로퍼티도 갖게 됨
name: "doldol"; // 타입 재정의도 가능
breed: string;
}
✨ 인터페이스 선언 합침
- 타입 별칭은 동일한 스코프 내 중복된 이름으로 선언 불가
type Person = {
name: string;
};
type Person = { // 중복된 이름으로 선언 불가
age: number;
};
- 인터페이스는 중복된 이름으로 선언 가능 → 인터페이스가 하나로 합쳐지므로 가능
- 동일한 이름의 인터페이스들이 동일한 이름의 프로퍼티를 다른 타입으로 정의했을 때는 오류 발생
interface Person {
name: string;
}
interface Person {
age: number;
}
// 위의 인터페이스들 합쳐져서 아래 인터페이스처럼 됨
interface Person {
name: string;
age: number;
}
'한 입 크기로 잘라먹는 타입스크립트' 핸드북을 정리한 내용입니다.
'FrontEnd' 카테고리의 다른 글
[타입스크립트] 한 입 크기로 잘라먹는 타입스크립트 정리 (6. 클래스 ~ 10. 유틸리티 타입) (1) | 2023.10.29 |
---|