Skip to main content

TS 1 Basic

Goal

타입스크립트를 사용하는데 기본기를 정리.

📌 Interface vs Type

Interface

  • objects, class의 구조를 정의하는데 사용.
  • 확장 가능 : extends를 이용해서 확장 가능하다.

Type

  • 데이터의 구체적인 유형을 정의 한다.
  • 확장 불가능 : reopened or extended 불가능 (extends 키워드가 불가능)

언제 사용 ?

  • interface : 공개 API를 정의하는 경우 인터페이스를 사용하여, 필요에따라 확장에 열려있게끔 한다.
  • interface : function overriding 가능

📌 Function overriding

// function types 
function add(a: number): (b: number) => number;
function add(a: number, b: number): number;
// 구현체는 모든 인터페이스에 대해서 대응해야 한다.
function add(a: number, b?: number): (b: number) => number | number {
if (b === undefined) return (b: number) => add(a, b);
return a + b;
}

📌 any, unknown, never

any

  • 모든 타입을 허용하는 타입이다.
  • 타입 검사나 오류를 무시 한다. 그래서 코드 안정성이 떨어질 수 있다.
let value: any;
value = 5;
value = "hello";
value = { name: "John" };

// 모든 속성이나 메서드에 접근 가능, 컴파일러가 오류를 잡아내지 않음
value.foo(); // 컴파일 오류 없음

unknown

  • 모든 타입을 허용하는 타입이다.
  • 타입 검사를 요구한다. 그래서 해당 변수를 사용하려면 타입검사가 필요하다.
  • asserts 을 이용해서 타입을 좁힐 수 있다.
let value: unknown;
value = 5;
value = "hello";
value = { name: "John" };

// 바로 접근할 수 없음, 타입 검사 필요
if (typeof value === 'string') {
console.log(value.toUpperCase()); // 타입 검사 후 안전하게 사용 가능
}

// 타입 단언을 통해 사용 가능
(value as { name: string }).name;

never

TypeScript에서 아무 값도 가질 수 없는 타입을 의미

  • 에러 처리, 무한 루프, 디스크리미네이트된 유니언 타입

Usecase of never type

//1.
//에러를 던지는 함수는 정상적으로 값을 반환하지 않기 때문에, 반환 타입을 never로 설정
function throwError(message: string): never {
throw new Error(message);
}
//사용 예시
throwError("Something went wrong"); // 이 함수는 종료되지 않고 에러를 던집니다.

//2.
//무한 루프 함수의 반환 타입으로서의 never
function infiniteLoop(): never {
while (true) {
console.log("Running forever...");
}
}

//3.Discriminated Union 타입에서의 never
type Shape =
| { kind: "circle"; radius: number }
| { kind: "square"; sideLength: number }
| { kind: "rectangle"; width: number; height: number };

function getArea(shape: Shape): number {
switch (shape.kind) {
case "circle":
return Math.PI * shape.radius ** 2;
case "square":
return shape.sideLength ** 2;
case "rectangle":
return shape.width * shape.height;
default:
const _exhaustiveCheck: never = shape; // 모든 케이스를 처리했는지 검사
return _exhaustiveCheck;
}
}

활용: Limiting prop composition

상호베타적인 타입을 선언할때 사용한다.

type ButtonProps = {
children: string;
};

type PrimaryButtonProps = {
primary: boolean;
secondary?: never;
};

type SecondaryButtonProps = {
primary?: never;
secondary: boolean;
};

const buildClassNames = (classes: { [key: string]: boolean }): string => {
let classNames = "";
for (const [key, value] of Object.entries(classes)) {
if (value) classNames += key + " ";
}
return classNames.trim();
};

export const Button = ({
children,
primary = false,
secondary = false,
}: ButtonProps & (PrimaryButtonProps | SecondaryButtonProps)) => {
const classNames = buildClassNames({ primary, secondary });

return <button className={classNames}>{children}</button>;
};

---
import { Button } from "./components/button";
import "./App.css";

function App() {
return (
<div className="container">
<Button primary>Primary Button</Button>
<Button secondary>Secondary Button</Button>
{/* 아래 2가지 props는 동시에 사용 불가능하다. */}
{/* <Button primary secondary>Secondary Button</Button> */}
</div>
);
}

export default App;

📌 enum vs obj as const

// 1.
// Js와 호환을위해 실제 객체를 만든다.
// 단점 : 하지만 enum 은 IIFE 로 변환되어 객체를 만들며, tree shaking이 안되는 문제가 있다.
enum COLORS {
Red = 'red',
Green = 'green',
Blue = 'blue',
PeRed = 'University of Pennsylvania red'
}

let currentColor = 'red'
const isPeRed = currentColor === COLORS.Red
console.log(isPeRed) // true

//2.
// const enum
// - 컴파일 단계에서 코드에 직접 상수를 넣어준다.
// - 별도의 설정이 필요하다.

//3.
// obj as const
const CountryMap = {
대한민국: 'SOUTH KOREA',
중국: 'CHINA',
일본: 'JAPAN',
미국: 'UNITED STATES',
북한: 'NORTH KOREA',
러시아: 'RUSSIA',
프랑스: 'FRANCE',
영국: 'ENGLAND',
} as const;

const key2 = CountryMap.대한민국