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.대한민국