Notice
Recent Posts
Recent Comments
Link
«   2024/09   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30
Tags
more
Archives
Today
Total
관리 메뉴

동그란 도그린

[타입스크립트] 한 입 크기로 잘라먹는 타입스크립트 정리 (1. 타입스크립트 개론 ~ 5. 인터페이스) 본문

FrontEnd

[타입스크립트] 한 입 크기로 잘라먹는 타입스크립트 정리 (1. 타입스크립트 개론 ~ 5. 인터페이스)

도그rin 2023. 10. 7. 16:59

1️⃣ 타입스크립트 개론

타입스크립트란?

기존의 자바스크립트를 더 안전하게 사용할 수 있도록 타입과 관련된 여러 기능(타입 지정 등)을 추가한 언어 (자바스크립트의 확장판) ⇒ 타입스크립트에서 자바스크립트 기본 문법들은 모두 사용 가능

 

타입스크립트 등장 배경

자바스크립트는 처음에 간결한 일부 프로그램을 만들기 위해 사용되었기 때문에 아래와 같은 특성을 가짐

  • 유연한 문법
  • 버그 발생 가능성 높음
  • 자유로움

하지만 Node.js(자바스크립트 런타임)이 탄생하며 자바스크립트를 어디서든 실행할 수 있게 되면서 다양한 프로그램을 만드는 데 사용됨

⇒ 자바스크립트가 유연해서 프로그램을 안정성을 떨어뜨림!

⇒ 이를 극복하기 위해 안정성을 추가한 타입스크립트가 등장

 

타입 시스템이란?

프로그래밍 언어를 사용할 때 타입과 관련해서 지켜야 하는 규칙들을 모아둔 문법 체계

  1. 정적 타입 시스템 (코드 실행 전 모든 변수의 타입을 고정적으로 결정) - C, Java
  2. 동적 타입 시스템 (코드 실행 후 그때그때 변수의 타입을 결정, 현재 변수에 담긴 값에 따라 변수의 타입이 계속 달라짐) - 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가지
      1. object로 정의 (⇒ 프로퍼티에 접근하려 하면 오류)
      2. 객체 리터럴 타입으로 정의
    // object로 정의
    let user: object = {
    	id: 1,
    	name: "이정환",
    };
    
    // 객체 리터럴
    let user: {
    	id: number;
    	name: string;
    } = {
    	id: 1,
    	name: "이정환",
    };
    
  • 열거형 (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 타입으로 추론됨

 

✨ 타입 추론 가능한 경우

  1. 변수 선언
    • 초기값을 기준으로 타입 추론
  2. 구조 분해 할당
    • 객체와 배열을 구조 분해 할당하는 경우
  3. 함수의 반환 값
    • return 문을 기준으로 추론
  4. 기본 값이 설정된 매개변수
    • 기본 값 기준으로 추론

 

✨ 주의할 상황

  1. 암시적으로 any 타입으로 추론
    • 변수 선언 시 초기 값을 생략하면 any 타입으로 추론됨 (매개변수의 타입이 any로 추론될 때와 달리 일반 변수의 타입이 any로 추론될 때는 오류로 판단되지 X)
  2. const 상수의 추론
    • const로 선언된 상수도 타입 추론 진행됨
    • 상수는 초기 값을 변경할 수 없으므로 좁은 타입으로 추론됨

 

✨ 타입 단언

  • 값 as 타입으로 특정 값을 원하는 타입으로 단언 가능
  • 다음 조건 만족 시 단언 가능 (A as B)
    1. A가 B의 슈퍼 타입일 때
    2. 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 단언

 

✨ 타입 가드

  • 조건문을 함께 사용해 타입을 좁히는 표현들
  1. instanceof 타입 가드 (Instanceof는 우리가 직접 만든 타입에는 사용 불가)
  2. 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;
}

 


'한 입 크기로 잘라먹는 타입스크립트' 핸드북을 정리한 내용입니다.

 

 

Comments