리액트

[TypeScript] 타입스크립트 정복하기

코딩하는둥이 2025. 4. 18. 17:32

타입스크립트란?

자바스크립트를 기반으로 한 강력한 정적 타입 지정 언어입니다. 이는 자바스크립트의 모든 기능을 포함하면서도, 추가적인 타입 시스템과 도구를 제공하여 코드의 안정성과 생산성을 높이는 데 도움을 줍니다.

 

정적 타입 검사

 컴파일 단계에서 코드의 타입 오류를 발견할 수 있어 런타임 오류를 줄입니다.

let age: number = 25;
age = "twenty-five"; // 오류 발생: 문자열을 숫자에 할당할 수 없음

 

자동 완성 및 코드 탐색

타입 정보를 기반으로 IDE에서 자동 완성과 코드 탐색 기능을 제공합니다.

const user = { name: "Alice", age: 30 };
console.log(user.name); // 자동 완성 지원

 

자바스크립트로 컴파일 

타입스크립트는 브라우저나 Node.js에서 실행 가능한 순수 자바스크립트로 컴파일됩니다.

 

타입 추론

 변수의 초기값을 기반으로 타입을 자동으로 추론합니다.

let name = "John"; // 타입이 string으로 자동 추론

 

 

자바스크립트 vs 타입스크립트

 자바스크립트는 동적타입 언어이기 때문에 변수 타입은 런타임에 따라 결정됩니다.

let value ="hi";
console.log(value.toUpperCase()); // hi
value = 1;
console.log(value.toUpperCase()); // ?

 

타입스크립트는 정적타입 언어라서 변수의 타입이 컴파일 타임에 결정이 돼서 타입 에러를 미리 발견할 수 있습니다.

 

장점

크로스브라우징 이슈 해결 

   크로스브라우징은 크롬, 사파리, 엣지, 파이어폭스 등 모든 브라우저에서 의도한 대로 이상없이 구현되도록 합니다.

   컴파일 과정에서 es6+ 문법들을 es5나 es3로 알아서 바꿔주기 때문에 구브라우저에도 대응이 가능합니다. 

 

단점

 작은 규모의 프로젝트에서는 불필요할 수 있고 작성해야할 코드가 많아질 수 있습니다.

 

object 타입

interface PersonType {
  name: string;
  age: number;
  isStudent: boolean;
  num?:number // 옵셔널 타입이라 없어도 되고 있어도 될 때 사용합니다.
}

let value: string = 'hello';

const person: PersonType = {
  name: '서개발',
  age: 10,
  isStudent: true,
}

const getPersonAge = (person: PersonType) => {
  return person.age;
}

// 사용 예시
console.log(getPersonAge(person)); // 10

 

array 타입

 :배열은 길이 제한이 없고, 모든 요소가 동일한 타입이어야 합니다.

let arr: (number | string)[] = [1, "two", 3];

 

Tuple 

 : 배열과 유사하지만, 요소의 타입과 개수가 고정되어 있습니다.
 : 각 요소의 타입과 순서를 명확하게 지정해야 하며, 순서가 다르면 에러 발생합니다.

let tuple: [string, number, boolean] = ["홍길동", 15, false];

let person: [string, number] = ["Alice", 30]

 : 선택적 요소와 나머지 요소도 지정 가능합니다.

let tupleWithOptional: [string, number?] = ["Alice"];

let tupleWithRest: [string, ...number[]] = ["Alice", 10, 20];

 

Enum

 : 관련된 상수 값들을 하나의 타입으로 묶는 기능입니다.

enum Country { Korea, USA, Japan, China }

let nation: Country = Country.Korea; // 0

 

명시적으로 값 할당 가능:

enum Country { Korea, USA = 100, Japan, China }

Country.Japan // 101

 

 

any 타입

 : 모든 타입의 값을 허용하는 특수 타입입니다. 변수에 어떤 값이든 할당할 수 있으며, 타입 검사가 완전히 무시됩니다.

let data: any = "hello";
data = 42; // 에러 없음
data = true; // 에러 없음

 


void 타입

값을 반환하지 않는 함수의 반환 타입을 나타냅니다. 주로 부수 효과(side effect)만 있는 함수에 사용합니다.\

function logMessage(msg: string): void {
  console.log(msg);
}

 

never 타입

절대 값이 발생할 수 없는(존재하지 않는) 타입입니다. 즉, "불가능"을 표현하는 타입입니다.

//항상 예외를 던지는 함수

function throwError(msg: string): never {
  throw new Error(msg);
}

//무한 루프 함수
function infiniteLoop(): never {
  while (true) {}
}
//switch문의 exhaustive 체크
type Shape = { type: 'circle' } | { type: 'square' };
function getArea(shape: Shape) {
  switch (shape.type) {
    case 'circle': return 1;
    case 'square': return 2;
    default:
      const _exhaustiveCheck: never = shape; // 새로운 타입 추가 시 에러 발생
      throw new Error('Unhandled case');
  }
}

 

undefined

 :변수에 값이 할당되지 않았을 때 자동으로 부여되는 값입니다. 즉, "아직 초기화되지 않은 상태"를 의미합니다.

let a;
console.log(a); // undefined

 

null

 : "값이 의도적으로 없음"을 개발자가 명시적으로 할당할 때 사용하는 값입니다.\

let b = null;
console.log(b); // null

 

 

symbol

 : ES6에 새롭게 추가되었고, number나 string과 기본 데이터 타입(원시타입)입니다.

   변경이 불가능한 유일한 값입니다.

1) 유일성

  :  Symbol()로 생성된 심볼은 모두 서로 다르며, 같은 문자열을 넘기더라도 서로 다른 심볼입니다.

const sym1 = Symbol('key');
const sym2 = Symbol('key');
console.log(sym1 === sym2); // false

 2) 변성

 : 생성 후 변경할 수 없습니다. 즉, 값이 고정되어 있으며 변경 불가합니다.

 

3) 암묵적 형변환 불가

: 심볼은 문자열이나 숫자와 달리 암묵적 형변환이 불가능하며, 문자열로 변환하려면 명시적 호출이 필요합니다.

const sym = Symbol('test');
// console.log(sym + ''); // 에러
console.log(sym.toString()); // 'Symbol(test)'

 

심볼은 유일한 값을 가지기 때문에 키값으로 사용하면 좋습니다.

//기본 선언

let sym: symbol = Symbol('mySymbol');

//심볼과 객체 프로퍼티
const mySym = Symbol('prop');
const obj = {
  [mySym]: 'value'
};

//심볼을 통한 타입 안전성 강화
const sym1: unique symbol = Symbol('unique');
const sym2: unique symbol = Symbol('unique');
// sym1 === sym2 // false, 고유 심볼
unique symbol: 타입스크립트에서 유일한 심볼 타입으로, 선언 시 const 또는 let에 unique symbol을 명시해야 함.


const MY_SYMBOL: unique symbol = Symbol('myUnique');

 

 

unknown 타입

 :  단어 그대로 타입이 뭔지 알 수 없는 타입입니다.

    어떤 값이든 들어올 수 있으니 엄격하게 검사해서 사용해야하며 any타입과 유사하지만, 훨씬 안전합니다.

    타입을 특정해주기 전까지는 무언가를 수행하려고 할 때 에러를 내줍니다.

let value: unknown;
value = true;
value = 42;
value = "Hello";
value = [];
value = {}; 
value = null;
value = undefined;
value = new Error();
value = Symbol("sym");

// 다른 타입 변수에 할당 시 오류 발생:
let value: unknown;
let num: number = value; // 에러!
let str: string = value; // 에러!

function isString(value: unknown): value is string {
  return typeof value === 'string';
}

function process(value: unknown) {
  if (isString(value)) {
    // 타입 가드로 인해 string으로 안전하게 사용 가능
    console.log(value.toUpperCase());
  } else {
    console.log("Not a string");
  }
}

 

함수선언하기 

1) 함수 선언식 

function add(a: number, b: number): number { // :number은 생략가능
  return a + b;
}

 

타입의 엘리먼트

 :  배열의 각 위치에 어떤 타입의 값이 들어가야 하는지 명확하게 제한할 수 있습니다.

type ElementType =[string, number, number];

const menu1:MenuType = ['치킨', 20000,2];

const getReceipt = (arr:MenuType[]):string => {
	return '총 얼마입니다';
}
getReceipt([menu1])

 

2) 함수 표현식

const add = function(a: number, b: number): number {
  return a + b;
};

 

3) 화살표 함수

typescript
const add = (a: number, b: number): number => a + b;

 

타입추론

 : 개발자가 구동으로 명시해야하는 타입 구문의 수를 줄일 수 있습니다.

   코드의 전체적인 안정성이 향상됩니다.

   어떤 변수에 타입이 다른 값을 재할당하기보다는, 별도의 변수를 사용하는 것이 바람직합니다.

타입추론
함수 타입추론
리턴 값 타입 추론

 

함수를 자체를 인자로 보낼 때 함수타임을 지정을 많이합니다.

const someFunc = (msg:string, showMsg:ShowMsgType) = >{
	showMsg(msg.toUpperCse();
}

someFunc('hello;,showMsg);

 

선택적 매개변수와 기본 매개변수

 매개변수 =  pararmeter

 인자 = argument

const sum = (a:number. b:number) => { //a,b매개변수
	return a+b;
}
sum(3,5) // 3,5 인자

 

선택적 매개변수 Optional Parameter

 : 매개변수의 개수는 가변적이라 선택적으로 받을 수 있습니다.

const num =1234;
num.toFixed();
num.toFixed(3);
const func = (nmae:string, job?:string) => { // 선택적으로 받을 인자만 ?를 붙여주면된다.
 console.log(name + '' + job);
}
func('김개발');

// 김개발 undefined

 

기본 매개변수  Default Parmeter

const func = (nmae:string, job = 학생) => { //인자값이 없으면 학생을 반환합니다.
 console.log(name + '' + job);
}
func('김개발');

// 김개발 학생

 

나머지 매개변수 Rest Parameter

  : 매개변수의 개수가 가변적일 때 배열 형태로 받아올 수 있습니다.

    무조건 파라미터 마지막에 있어야합니다.

const func = (one:number, two: number, ...rest : number[]) => {
 console.log(one);
 console.log(two);
 console.log(rest);
}
func(1,2,3,4,5);

// 1
// 2
//[3,4,5]

 

 

유니언 타입 

 : 여러 타입 중 하나가 될 수 있음을 명시하는 타입입니다.

cosnst func = ( id:string | number) => {
	console.log('ID', id)
}
func('1234');
func(1234);

// 'ID', '1234'
// 'ID', 1234

 

 

교차타입

 : 여러 타입을 모두 만족하는 하나의 타입을 만드는 것입니다.

type Person = { 
	name:string;
    age:number;
}

type Student = Person & {
 school:stirg;
}

 

 

타입 앨리어스(Type Aliases)

 : 번역하면 타입 별칭이라고 하는데 중복되거나 복잡한 타입들은 이름을 지어줘서 재사용합니다.

type Person = {
	name:string;
    age:number;
}

const user1:Person = {
	name:'서개발',
    age: 25
}

const user2:Person = {
	name:'둥개발',
    age: 21
}

 

유니언 타입으로 타입 앨리어스 주는 방법입니다.

type studentId = number | null;
let id:studentId = null;

 


교차 타입으로 타입 앨리어스 주는 방법입니다.

type studentId = number | null;

const user1:Person & {
	id: studentId;
}

 


투플 타입으로 타입 앨리어스 주는 방법입니다.

type TupleType = [number, string];
const tuple1:TupleType = [10,'hi'];

 

 

타입앨리어스 vs 인터페이스

//  타입 앨리어스
type Person = {
    name:string;
    age:number;
}

// 인터페이스
interface Person2 {
    name:string;
    age:number;
}

 

확장 시 

// 타입 앨리어스 교차 형식으로 확장
type Student = Person & {
	id : number
}

// 인터페이스 확장
inferface Student2 extends Person2{
	id:number
}

 

타입앨리어스는 같은 이름을 사용하면 에러가 뜨지만, 인터페이스는 에러가 발생하지 않고 합쳐지게 됩니다. => 선언적 확장

타입 앨리어스는 &, 인터페이스는 extends로 확장이 가능합니다.

인터페이스는 동일한 이름으로 정의함으로써 선언적 확장이 가능합니다.

인터페이스는 객체에만 사용이 가능합니다. 

 

 

리터럴 타입

 : 고정된 값을 타입으로 가집니다.

 

특정 값 1만 가진다.

 

원래같으면 string으로 지정하지만 혈액형은 4가지 종류만 있으니 아래의 코드처럼 리터럴 타입으로 주는 것이 정확합니다.

type BloodType = 'A' | 'B' | 'AB' | 'O'

 

 

만약에 위에 있는 코드가 수정될 일이 없으면 뒤에 as const를 붙이면 됩니다.

 

 

 

배열일 때도 수정할 일 없고 고정일 때만 as const를 사용합니다.

 

유니업타입

 인터페이스 주석에 있는 값만 받으려고 했는데 다른 값을 받으면 오류가 날 수 있습니다. 

interfacr Item {
	code: number; // 280 , 281 , 282 , 283
    size:string; // small, medium, large, xlargr
}

const SelectedItem: Item = {
	code:0,
    size:'XL'
}

 

그래서 리터럴 타입으로 변경을 하면 오류가 안 생기고 주석도 안  달아도 됩니다.

interfacr Item {
	code: 280 | 281 | 282 | 283
    size: small| medium | large | xlargr
}

const SelectedItem: Item = {
	code:0, // 에러
    size:'XL'  // 에러
}

 

 

클래스

class User {
  name: string;
  age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }

  greet(): string {
    return `Hello, ${this.name}!`;
  }
}

const user = new User('Alice', 30);
console.log(user.greet()); // Hello, Alice!

 

클래스 상속

 : 상속을 통해 하위 클래스가 상위 클래스의 프로퍼티와 메서드를 상속받을 수 있습니다.

   상위 클래스의 생성자의 호출을 하기 위해 하위 클래스의 생성자에거 super()  메서드를 사용해야합니다.

class Animal {
  name: string;

  constructor(name: string) {
    this.name = name;
  }

  speak(): void {
    console.log(`${this.name}이(가) 소리를 냅니다.`);
  }
}

class Dog extends Animal {
  constructor(name: string) {
    super(name); // 부모 생성자 호출
  }

  speak(): void {
    console.log(`${this.name}이(가) 멍멍 짖습니다.`);
  }
}

const dog = new Dog('바둑이');
dog.speak(); // 바둑이이(가) 멍멍 짖습니다.

 

readonly 지정자

 : constructor 외부에서 값 할당이 불가능하게 막습니다.

   속성(프로퍼티)이나 배열, 튜플, 객체 타입의 값을 "읽기 전용"으로 만들어주는 키워드입니다. 한 번 값이 할당되면 이후에는 변경할 수 없도록 하여, 코드의 **불변성(immutability)**을 보장하고, 실수로 인한 값 변경을 방지할 수 있습니다

class Person {
  readonly name: string;
  constructor(name: string) {
    this.name = name; // 생성자에서만 할당 가능
  }
}

const john = new Person('John');
john.name = 'Jane'; // 오류: 'name'은 읽기 전용 속성입니다.

 

 

getters / setters

private으로 설정된 프로퍼티를 읽거나 새로운 값을 할당하고 싶을 때는 getters / setters를 사용하면 됩니다.

class User {
  private _age: number = 24;

  // getter: 값을 읽을 때 사용
  get age() {
    console.log('Age getter called');
    return this._age;
  }

  // setter: 값을 쓸 때(할당할 때) 사용
  set age(value: number) {
    console.log('Age setter called');
    if (value < 0) {
      throw new Error('나이는 0보다 작을 수 없습니다.');
    }
    this._age = value;
  }
}

const user = new User();
user.age = 25;          // Age setter called
console.log(user.age);  // Age getter called, 25

 

 

전역 프로퍼티

 : static 키워드를 사용하고 인스턴스를 생성하지 않고도 클래스의 메서드 프로퍼티에 접근이 가능합니다.

class Counter {
  static count: number = 0;

  static increment() {
    Counter.count++;
  }
}

// 인스턴스 생성 없이 클래스명으로 접근
console.log(Counter.count); // 0
Counter.increment();
console.log(Counter.count); // 1

const c = new Counter();
// 인스턴스를 통해서는 static 프로퍼티에 접근 불가
// console.log(c.count); // 오류!

 

추상 클래스

 : 클래스, 클래스의 메서드에 abstract 키워드를 사용해서 표현합니다.

   추상 메서드는 정의만 되어 있고, 구현은 되어 있지 않습니다.

   추상 클래스는 인스턴스를 만들 수 없습니다.

   추상 클래스를 상속받은 하위 클래스에서 추상 클래스 내에 정의된 추상 메서드를 반드시 구현해야 합니다.

// 추상 클래스 선언
abstract class Animal {
  constructor(private _age: number) {}

  get age() {
    return this._age;
  }
  set age(value: number) {
    this._age = value;
  }

  // 추상 메서드: 구현 없음, 반드시 하위 클래스에서 구현해야 함
  abstract makeSound(): void;

  // 구현된 메서드: 공통 동작 정의 가능
  sleep() {
    console.log('Zzz...');
  }
}

// 추상 클래스를 상속받는 구체 클래스
class Dog extends Animal {
  makeSound(): void {
    console.log('멍멍!');
  }
}

const dog = new Dog(3);
dog.makeSound(); // 멍멍!
dog.sleep();     // Zzz...

// const animal = new Animal(5); // 오류! 추상 클래스는 직접 인스턴스화 불가

 

오버라이팅(overriding)

 : 상위 클래스에서 정의되었던 메서드를 하위클래스에서 재정의하는 것입니다.  

   상위 클래스에 동일한 이름의 메서드를 무시하고 하위클래스에서 재정의할 필요가 있을 때, 추상 클래스에서 정의한 추상메서드를 구현할 때 사용합니다.

class Person {
  sayHello() {
    console.log("Hello");
  }
}

class Student extends Person {
  sayHello(name?: string) {
    if (name === undefined) { // 오타: underfined → undefined
      super.sayHello();
    } else {
      console.log('hi ' + name); // 오타: consoel → console
    }
  }
}

const s = new Student();
s.sayHello();         // Hello
s.sayHello("철수");   // hi 철수

 

제네릭

 단일 타입이 아닌 다양한 타입에서 작동하는 컴포넌트를 작성할 수 있습니다.

 사용처에서 타입을 정해서 전달할 수 있습니다.

function identity<T>(arg: T): T {
  return arg;
}

// 사용처에서 타입 지정
const a = identity<string>("hello"); // a: string
const b = identity<number>(123);     // b: number
const c = identity<boolean>(true);   // c: boolean

 

 

제네릭 타입 변수

함수나 클래스, 인터페이스에서 타입을 변수처럼 선언해 두고,
 함수를 사용할 때(호출 시점에) 원하는 타입을 지정할 수 있도록 해줍니다.

 

제네릭 타입 매개변수 <T>를 사용하면 함수 호출 시 타입을 지정해 T[] 타입의 배열을 안전하게 처리할 수 있고, any와 달리 타입 체크가 정확하게 이뤄집니다

function identity<T>(arg: T[]): T[] {
  console.log(arg.length); // 배열 길이 출력
  return arg;
}

let output = identity<number>([1, 2, 3]); // 출력: 3, 반환 타입: number[]

 

제네릭 타입 매개변수를 두개 사용할 수 있습니다.

function identity<T, U>(arg1: T, arg2: U): [T, U] {
  return [arg1, arg2];
}

const result = identity<number, string>(3, 'hi'); // 반환 타입: [number, string]

 

제네릭 클래스

 : 다양한 타입의 데이터를 안전하게 저장하고 관리할 수 있습니다.

 

 number 타입만 저장하는 리스트를 만들고, 숫자 1을 추가한 뒤 결과를 출력합니다.

class List<T> {
  data: T[];
  constructor(data: T[]) {
    this.data = data;
  }
  add(item: T) {
    this.data = [...this.data, item]; 
  }
}

const list = new List<number>([]);
list.add(1);
console.log(list.data); // [1]

 

제네릭 인터페이스

 : 인터페이스 정의 시 타입 매개변수를 사용해, 속성의 타입을 유연하게 지정할 수 있습니다

   id를 number 타입으로 지정해 객체를 생성합니다.

interface Person<T> {
  name: string;
  id: T;
}

let person: Person<number> = {
  name: 'asd',
  id: 10
};

 

 

제네릭 제약 조건 

 : 네릭 타입 매개변수가 가질 수 있는 타입의 범위를 제한하는 기능입니다.
   기본적으로 제네릭은 모든 타입을 받을 수 있지만, 때로는 특정 속성이나 구조를 가진 타입만 허용하고 싶을 때가 있습니다.
    이럴 때 extends 키워드를 사용해 제약을 걸 수 있습니다

interface LengthWise { 
  length: number;      
}

function identity<T extends LengthWise>(arg: T): T {
  console.log(arg.length); // 정상: length 속성 접근
  return arg;
}

identity({ name: 'se', length: 100 });

 

 

타입 단언(as)

 타입 추론이 정확하지 않을 때, 혹은 의도와 다르게 추론하고 있을 때, as키워드를 사용해서 타입을 단언해줄 수 있습니다.

 

아래 코드는 a는 string 타입이니깐 가지고 오라는 뜻입니다.

let a:unknown = "hi";
 let b = (a as string).length;

 

아래 코드는 컴파일러는 myCanvas가 어떤 HTMLElement인지 확실히 모르지만, 개발자가 직접 HTMLCanvasElement라고 단언해 타입 오류를 방지합니다.

const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;

 

타입 서술어(is)

 : 특정 값이 원하는 타입임을 타입스크립트에게 알려주는 기능입니다.

 

isFish 함수는 pet이 Fish 타입인지 런타임에 검사해, 이후 코드에서 타입을 안전하게 좁혀주는 타입 가드 역할을 합니다.

type Fish = { swim: () => void };
type Bird = { fly: () => void };

function isFish(pet: Fish | Bird): pet is Fish { //pet is Fish 타입가드
  return (pet as Fish).swim !== undefined;
}

const fish: Fish = { swim: () => console.log("물고기 수영") };
const bird: Bird = { fly: () => console.log("새 날기") };

console.log(isFish(fish)); // true
console.log(isFish(bird)); // false

타입 가드란, 어떤 스코프 안에서 특정 타입을 보장하는 런타임 검사를 수행하는 표현식입니다.

 

 

in 연산자 

 : 자바스크립트에서 객체안에 어떤 속성이 있는지 여부를 확인할 때 in 연산자를 사용합니다.

 

'swim' in pet 타입 가드를 사용하면 Fish와 Bird 타입을 안전하게 구분해 각 타입의 메서드를 호출할 수 있습니다.

type Fish = { swim: () => void };
type Bird = { fly: () => void };

// 1. 타입 가드 함수 
function isFish(pet: Fish | Bird): pet is Fish {
  return 'swim' in pet;
}

// 2. 동물 객체
const fish: Fish = { swim: () => console.log("물고기 수영") };
const bird: Bird = { fly: () => console.log("새 날기") };

// 3. 동작 함수
function action(pet: Fish | Bird) {
  if ('swim' in pet) { 
    pet.swim(); // Fish 타입으로 추론
  } else {
    pet.fly();  // Bird 타입으로 추론
  }
}

 

 

 

_instanceof 

 : object의 프로토 타입 프로토타입 체인에 constructor.prototype이 존재하는지 판별합니다.

object: 판별할 객체

constructor: 판별 목표함수

object instanceof constructor

 

instanceof 연산자를 사용하면 객체가 특정 클래스의 인스턴스인지 확인할 수 있습니다.

class User {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}

const hello = new User('hello');
console.log(hello instanceof User); // true

 

instanceof를 통해 객체가 User인지 Staff인지 구분하고, 해당 속성에 안전하게 접근할 수 있습니다.

class User {
  id = 100;
  name = 'user';
}

class Staff {
  staffId = 200;
  name = 'staff';
}

function getId(arg: User | Staff) {
  if (arg instanceof User) {
    console.log(arg.id); // User 인스턴스면 id 출력
  }
  if (arg instanceof Staff) {
    console.log(arg.staffId); // Staff 인스턴스면 staffId 출력
  }
}

// 사용 예시
const user = new User();
const staff = new Staff();
getId(user);  // 100
getId(staff); // 200

 

인데스 시그니처

 : 객체의 키 이름을 아직 모르고 있고, 타입만 알고 있는 경우에 사용합니다.

interface UserType{
    [index:string]: number | string; // 유니언 타입으로 사용하면 됩니다.
    length:number;
    name: string;// name에 string을 넣으면 [index:string]: number;와 충돌이 일어납니다.
}

const user:UserType = {
    'name' : '서지발',
    'age' :20 // 작음 따음표는 제거가 가능하지만 없는 키에도 접근가능한 단점이 있습니다.
}

* 어떤 타입이 올지 알수 있는 상황이라면, 인덱스 시그니처를 사용하기보다는 최대한 정확한 타입을 정의하는 것이 낫습니다.

 

인덱스 타입 쿼리 연산자 keyof

 : 객체 타입의 키들을 리터럴 유니언 타입으로 추출하는 연산자입니다. 즉, 객체 타입의 모든 키 이름을 하나의 타입(유니언 타입)으로 만들어줍니다

type Point = {x:number; y:number;};
type p = keyof Point; // type P = 'x' | 'y';와 같습니다.


//인덱스 시그니처가 있을 때
type Arrayish = {[n:number]: unknown};
type A  = leyof Arrayish; // type A = number;와 같습니다.
// 숫자 인덱스 시그니처가 있으면, keyof는 해당 인덱스 타입(number)을 반환합니다.


type Mapish = { [k: string]: boolean };
type M = keyof Mapish; // string | number;
//문자열 인덱스 시그니처가 있으면, 자바스크립트 객체 키가 항상 문자열로 변환되기 때문에 string | number가 됩니다

 

객체 리터럴에서 typeof와 함께 사용하겠습니다.

typeof로 객체의 타입을 추출하고, keyof로 키 유니언 타입을 만들 수 있습니다.
as const를 붙이면 모든 프로퍼티가 readonly 및 리터럴 타입으로 고정됩니다.

 

const DevelopStatus = {
  ready: '준비중',
  develop: '개발중',
  complete: '개발완료',
} as const;

type StatusKey = keyof typeof DevelopStatus; // "ready" | "develop" | "complete"
let myStatus: StatusKey = 'ready';

 

매핑 타입 

 : 중복을 피하기 위해서 기존 정의된 타입을 새로운 타입으로 변환하는 문법입니다. 타입을 매핑 시키는 방법입니다.

제네릭 타입 T의 모든 프로퍼티 이름을 그대로 가져와서, 각 프로퍼티의 타입을 boolean으로 바꿔주는 타입입니다.만약 T가 { a: number; b: string }라면, OptionsFlags는 { a: boolean; b: boolean }이 됩니다.

type OptionsFlags<T> = {
  [Property in keyof T]: boolean;
}
type Input = {
  value: string;
  onChange: () => void;
  disabled: boolean;
}

type InputOptions = OptionsFlages<Input>
//type InputOptions = {
//	value:boolean;
//    onChange: boolean;
//    disabled:booledn;
//}

 

readonly 수정자를 제거하여 모든 프로퍼티를 수정 가능하게 변환하는 유틸리티 타입입니다.

type CreateMutable<T> = {
	-readonly [Property in keyof T[: t[Property];
}

 

조건부 타입 

 : 타입을 조건적으로 부여할 수 있습니다.

SomeType extends OtherType ? TureType : falseType;

 

Student 타입이 User 타입에 할당될 수 있다면 number이 되고 안되면 string이되도록 합니다.

interface User { name: string }
interface Student extends User { studentId: number }

type Example = Student extends User ? number : string  // 결과: number

 

T에 number가 들어가면 IdLabel 할당되고 T에 string이 들어가면 NameLable이 할당됩니다.

 

interface IdLabel { id: number }
interface NameLabel { name: string }

type NameOrId<T extends number | string> = T extends number 
  ? IdLabel 
  : NameLabel;

 

idOrName은 아직 타입이 안정해졌습니다(number또는 string타입) NameOrId은 들어온 타입에 따라 결정이 됩니다.
createLabel이 사용되는 곳에서 인자를 전달해줘야지 타입이 정해집니다.

function createLabel<T extends number | string>(idOrName: T): NameOrId<T> {
 throw "uniplemented";
}

 

 

분산조건부 타입

 : 제네릭 타입 위에서 조건부 타입은 유니언 타입을 만나면 분산적으로 작동합니다.

 

string | number와 같은 유니온 타입이 조건부 타입에 들어가면, 각각 분리되어 ToArray<string> → string[], ToArray<number> → number[]로 평가된 뒤 최종적으로 string[] | number[]로 결합됩니다.

type ToArray<t> = Textends any > T[] : never;
type StrArrOrNumArr = ToArray<string | number>

 

Test<T>에서 분산 조건부 타입이 동작해 number와 boolean을 개별 평가 → number는 조건 만족 → number, boolean은 불만족 → null → 최종 타입 number | null

type T = number | boolean;
type Test<T> = T extends number ? T : null;
type A = Test<T>; // 결과: number | null

 

 

lnfer 키워드

 : extends 키워드와 함께 사용되며, 타입스크립트가 런타입에서 타입을 추론하게 됩니다.

T extends infer U ? X : Y

 

T가 length 프로퍼티를 가지면 해당 프로퍼티의 타입(U)을 추론해 반환하고, 없으면 never를 반환합니다. 예시에서는 { length: number }가 입력되어 number 타입이 결과로 나옵니다.

type LengthType<T> = T extends { length: infer U } ? U : never;
type A = LengthType<{ length: number }>; // 결과: number
type Afunc = (x:string, y:number) => void;
type b = GetParmType<BFunc>;

 

 

유틸리티 타입

 : 타입을 쉽게 변환할 수 있습니다.

 

Partial<T> 

 : 모든 속성이 옵셔널로 변경된 타입을 반환합니다,

 

Partial<T>를 사용 전

interface SomeType {
	a: string;
    b: number;
}

 

Partial를 사용

 

type PartialType = Partial<SomeType>

 

Partial를 사용 후

type PartialType = {
  a?: string | undefined;
  b? number | undefined;
}

 

 

Readonly<T>

 : 모든 속성을 Readonly로 변경된 타입을 반환합니다. 

type ReadonlyUser = Readonly<User>; // { readonly name: string; readonly age: number }

 

Record<K,T>

 : 속성의 키가 K이고, 속성의 값이 T인 타입을 반환합니다.

type Grades = Record<"math" | "eng", number>; // { math: number; eng: number }

 

Pick<T,K>

 : 리터럴 타입으로 부분적인 프로퍼티들을 선택해 타입을 반환합니다.

type NameOnly = Pick<User, "name">; // { name: string }

 

Omit<T,K>

 :  리터럴 타입으로 부분적인 프로퍼티들을 선택해 타입으로 반환합니다.

type WithoutAge = Omit<User, "age">; // { name: string }

 

Exclude<T,U>

 : T타입에서 U타입을 제외시킨 타입을 반환합니다.

type T2 = Extract<"a" | "b" | 1, string>; // "a" | "b"

 

Extract<T,U>

 : U에 할당 가능한 모든 유니온 멤버를 T에서 가져와서 반환합니다.

type T3 = NonNullable<string | null | undefined>; // string

 

NonNullable<T>

 : T에서 null과 undefined를 제외하고 타입을 반환합니다.

type Fn = (a: number, b: string) => void;
type Params = Parameters<Fn>; // [number, string]

 

Parameters<T>

 : 함수의 매개변수에서 사용된 타입을 튜플 타입으로 반환합니다.

type Fn = (a: number, b: string) => void;
type Params = Parameters<Fn>; // [number, string]

 

ReturnType<T>

 : 함수의 리턴 타입을 반환합니다.

type Fn = () => number;
type Ret = ReturnType<Fn>; // number

 

Required<T>

 : 모든 속성이 required로 변경된 타입을 반환합니다.

type User2 = { name?: string; age?: number };
type RequiredUser = Required<User2>; // { name: string; age: number }