
✅ 공부한 내용 정리
 
타입스크립트는 자바스크립트의 상위 집합이다. 를 알아본다.// 자바스크립트 오류 O, 타입스크립트 오류 발생 X
function greet(who: string) {
  console.log('Hello', who);
}// 1. 자바스크립트 오류 X , 타입스크립트 오류 X(타입 체크 통과)
const x = 2 + '3'; // string 타입
const y = '3' + 2; // string 타입
// 두 줄 모두 문자열이 "23"이 되는 자바스크립트 런타임 동작으로 모델링 됨// 2. 자바스크립트 오류 X, 타입스크립트 오류 O(타입 체크 미통과)
const a = null + 7; // js -> a 값이 7, ts -> 오류(+ 연산자를 ~ 형식에 적용할 수 없습니다.)
const b = [] + 12; // js -> b 값이 12, ts -> 오류(+ 연산자를 ~ 형식에 적용할 수 없습니다.)
alert('Hello', 'ts'); // js -> 'Hello' 경고 표시, ts -> 오류(0~1개의 인수가 필요한데 2개를 가져왔습니다.)// 3. 자바스크립트 오류 O, 타입스크립트 오류 O(타입 체크 미통과)
const names = ['lee', 'kim'];
console.log(names[2].toUpperCase()); // TypeError 발생 -> 런타임 오류 발생💡 컴파일타임과 런타임과 에러
컴파일타임 - 개발자가 작성한 소스코드를 컴파일하여 기계어로 변환하는 과정
컴파일타임 에러 - syntax error(문법적 오류), 파일참조 오류, 타입체크 오류...
-> 컴파일러는 컴파일 타임 에러를 발생시키고 일반적으로 문제를 일으킨 소스코드 라인을 지시해줌
런타임 - 컴파일을 마친 프로그램이 사용자에 의해 실행되어 동작되어지는 때
런타임 에러 - 0나누기 오류, null참조 오류, 메모리 부족 오류
-> 프로그램이 실행중에 발생하는 형태의 오류로 사용자가 맞닥뜨릴 수 있으므로 조심해야 함
🎯 요약
- 타입스크립트는 자바스크립트의 상위집합이다.
모든 자바스크립트 프로그램은 이미 타입스크립트 프로그램이다.
타입스크립트에는 별도의 문법이 존재하기 때문에, 그 반대는 성립하지 않는다.
- 자바스크립트는 런타임에서 타입을 알게되고,
타입스크립트 타입 시스템은 자바스크립트 런타임 동작을 모델링 한다.
따라서 런타임으로 가기전에 타입검사를 함으로 런타임에 발생할 수 있는 오류를 먼저 잡아낼 수 있어 개발자에게 빠르게 알려줄 수 있다.
다만, 모든 오류를 찾을 순 없다.
(타입 체커를 통과하면서도 런타임 오류를 발생시킬 수 있음)
타입스크립트의 설정은
어디서 소스파일을 찾을건지, 어떤 종류의 출력을 생성할지를 제어, 언어 자체의 핵심요소를 제어 등등
또한 ts설정은 커맨드 라인보다는 tsconfig.json을 사용한다.
tsc --init
noImplicitAny- 변수들이 미리 정의된 타입을 가져야 하는지 여부를 제어
strictNullChecks- null과 undefined가 모든 타입에서 허용되는지 확인하는 설정
{
  "compilerOptions": {
    "noImplicitAny": true,
    "strictNullChecks": true,
  }
}strict설정을 고려한다.
- 최신 ts/js를 브라우저에서 동작할 수 있도록 구버전의 js로 트랜스파일(번역+컴파일)함
- 코드의 타입 오류를 체크
let x = 'hello';
x = 1234;
// tsc test.ts
// text.ts:2:1 - error TS2322: '1234'형식은 'string' 형식에 할당할 수 없습니다.// instanceof 체크는 런타임에 일어나지만,
// Rectangle은 타입이기 때문에 런타임 시점에 아무런 역할을 못함
interface Square {
  width: number;
}
interface Rectangle extends Square {
  height: number;
}
type Shape = Square | Rectangle;
function calculateArea(shape: Shape) {
  if (shape instanceof Rectangle) {
    // 오류: 'Rectangle'은 형식만 참조하지만, 여기서는 값으로 사용되고 있습니다.
    return shape.width * shape.height; // 오류: 'Shape' 형식에 'height' 속성이 없습니다.
  } else {
    return shape.width * shape.width;
  }
}❓ 태그된 유니온 (Tagged Union) 이란?
상황에 따라 인터페이스를 분리한 후 해당 인터페이스들을 type을 이용하여 유니온으로 사용한 것
태그는 type에 주어진 개별 속성이며 런타임 시 타입의 범위를 줄일 수 있도록 도와준다.
태그된 유니온은 런타임 타입 체커와 잘 맞기 때문에 필요 시 사용하자
interface Square {
  kind: 'square';
  width: number;
}
interface Rectangle {
  kind: 'rectangle';
  height: number;
  width: number;
}
type Shape = Square | Rectangle; // 태그된 유니온
function calculateArea(shape: Shape) {
  if (shape.kind === 'rectangle') {
    shape; // 타입이 Rectangle
    return shape.width * shape.height;
  } else {
    shape; // 타입이 Square
    return shape.width * shape.width;
  }
}// 2. 타입을 클래스로 만들기
class Square {
  constructor(public width: number) {}
}
class Rectangle extends Square {
  constructor(public width: number, public height: number) {
    super(width);
  }
}
type Shape = Square | Rectangle;
function calculateArea(shape: Shape) {
  if (shape instanceof Rectangle) {
    shape; // 타입이 Rectangle
    return shape.width * shape.height;
  } else {
    shape; // 타입이 Square
    return shape.width * shape.width; // 통과
  }
}string 또는 number 타입인 값을 항상 number로 정제하는 경우
function asNumber(val: number | string): number {
  return val as number;
}// 변환된 js 파일
function asNumber(val) {
  return val;
}function asNumber(val: number | string): number {
  return typeof val === 'string' ? Number(val) : val;
}// 여기서의 : boolean은 타입 선언문으로 타입 스크립트의 구문이기에 런타임시 제거되기 때문에,
//자바스크립트였다면 setLightSwitch('on') 으로 호출 할 수도 있었을 것입니다.
function setLightSwitch(value: boolean) {
  switch (value) {
    case true:
      turnLightOn();
      break;
    case false:
      turnLightOff();
      break;
    default:
      console.log(`I'm afraid I can't do that.`);
  }
}
// 순수 타입스크립트에서도 네트워크 호출로 값을 받아온 경우, 타입이 달라질 수도 있다.
interface LightApiResponse {
  lightSwitchValue: boolean;
}
async function setLight() {
  const response = await fetch('/light');
  const result: LightApiResponse = await response.json();
  setLightSwitch(result.lightSwitchValue);
  // API를 잘못 파악했거나 배포 후에 API가 변경됬을 때,
  // LightApiResponse가 문자열로 바뀌게 되는 경우가 있을 수 있다.
  // 그렇기 때문에 선언된 타입이 언제든지 달라질 수 있다는 것을 명심해야 한다.
}💡 오버로드 VS 오버로딩
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: any, b: any): any {
  return a + b;
}
console.log(add(2, 3)); // 출력: 5 (숫자형 오버로드 호출)
console.log(add('Hello, ', 'TypeScript!'));
// 출력: Hello, TypeScript! (문자열 오버로드 호출)class Calculator {
  add(a: number, b: number): number {
    return a + b;
  }
  add(a: string, b: string): string {
    return a + b;
  }
}
const calculator = new Calculator();
console.log(calculator.add(2, 3)); // 출력: 5 (숫자형 오버로딩 호출)
console.log(calculator.add('Hello, ', 'TypeScript!'));
// 출력: Hello, TypeScript! (문자열 오버로딩 호출)function add(a: number, b: number) {
  return a + b;
}
// ~~ 중복된 함수 구현입니다.
function add(a: string, b: string) {
  return a + b;
}
// ~~ 중복된 함수 구현입니다.하나의 함수에 대해 여러 개의 선언문을 작성할 수 있지만, 구현체는 오직 하나여야 한다.
function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a, b) {
  return a + b;
}
const three = add(1, 2); // 타입 number
const twelve = add('1', '2'); // 타입 string🎯 요약
- 코드 생성은 타입시스템과 무관하다.
타입스크립트 타입은 런타임 동작이나 성능에 영향을 주지 않는다.
- 타입 오류가 존재하더라도 코드 생성(컴파일)은 가능하다.
- 타입스크립트 타입은 런타임에 사용할 수 없다.
런타임에 타입을 지정하려면, 타입 정보 유지를 위한 별도의 방법이 필요하다. (태그된 유니온과 속성 체크 방법 또는 클래스)
💡 덕 타이핑 VS 구조적 타이핑
덕 타이핑 - 객체의 타입이나 클래스가 중요한 것이 아니라, 객체가 어떤 메서드나 속성을 지니고 있는지에 따라 타입이 결정된다는 개념입니다.
구조적 타이핑 - 객체들이 같은 구조를 가지면 같은 타입으로 간주되는 것을 의미합니다.
- 덕 타이핑은 런타임에 타입을 체크한다. (혹은 안할수도 있음)
- 구조적 타이핑은 타입 시스템 기반에서 컴파일 타임(혹은 타입체커)에서 타입을 체크한다.
- 즉, 둘 다 객체의 변수, 메소드 같은 필드를 기반으로 타입을 체크(혹은 안할수도)하지만 덕 타이핑은 동적 타이핑에서, 구조적 타이핑은 정적 타이핑에서 쓰인다.
- 덕 타이핑은 다형성 관점에서 주목해야하고, 구조적 타이핑은 타입 체킹 관점이다.
// NamedVector의 구조가 Vector2D와 호환되기 때문에 calculateLength 호출 가능
// -> 구조적 타이핑
function calculateLength(v: Vector2D) {
  return Math.sqrt(v * x + v * x + v * y + v * y);
}
interface NamedVector {
  name: string;
  x: number;
  y: number;
}
const v: NamedVector = { x: 3, y: 4, name: 'vector' };
calculatedLength(v); // 5interface Vector3D {
  x: number;
  y: number;
  z: number;
}
function calculateLengthL1(v: Vector3D) {
  let length = 0;
  for (const axis of Object.keys(v)) {
    const coord = v[axis]; // 오류 발생
    // 'string'은 'Vector3D'의 인덱스로 사용할 수 없기에 엘리먼트는 암시적으로 'any' 타입
    length += Math.abs(coord);
  }
  return length;
}
// v는 어떤 속성이든 가질 수 있어서 v[axis]가 number라고 확정 할 수 없다.class C{
  foo: string;
  constructor(foo: string){
    this.foo = foo;
  }
}
const c: new C('instance of C');
const d: C = { foo: 'object literal'}; // 통과
// 구조적으로는 필요한 속성과 생성자가 존재하기 때문에 문제가 없다.test('getAuthors', () => {
  const authors = getAuthors({
    runQuery(sql: string) {
      return [
        ['Toni', 'Morrison'],
        ['Maya', 'Angelou'],
      ];
    },
  });
});
expect(authors).toEqual([
  { first: 'Toni', last: 'Morrison' },
  { first: 'Maya', last: 'Angelou' },
]);🎯 요약
- 자바스크립트가 덕 타이핑 기반이고 타입스크립트가 이를 모델링하기 위해 구조적 타이핑을 사용함을 이해해야 한다.
어떤 인터페이스에 할당 가능한 값이라면 타입 선언에 명시적으로 나열된 속성들을 가지고 있다.
타입은 '봉인'되어 있지 않고, '열려'있다.
- 클래스 역시 구조적 타이핑 규칙을 따른다.
클래스의 인스턴스가 예상과 다를 수 있다.
- 구조적 타이핑을 사용하면 유닛 테스닝을 손쉽게 할 수 있다.
any 타입을 지양해야되는 6가지 이유
let age: number;
age = '12'; // '"12"' 형식은 'number' 형식에 할당 할 수 없습니다.
age = '12' as any; // 통과
age += 1; // 런타임에 정상, age는 "121"❓ 함수 시그니처란 ?
함수의 매개변수와 반환값의 타입을 정의하는 방법입니다.
// birthDate 매개변수는 string이 아닌 Date 타입 이어야 함
function calculateAge(birthDate: Date): number {
  //...
}
let birthDate: any = '1999-08-25'; // any 사용으로 인해 calculateAge 시그니처 무시
calculateAge(birthDate); // 통과자동완성 기능과 적절한 도움말(이름 변경 기능 등)을 제공 받지 못한다.
onSelectItem 콜백이 있는 컴포넌트가 존재any를 써보자onSelectItem에 id만 필요하니까 ComponentProps의 시그니처를 수정id를 전달받으면, 타입 체커를 통과함에도 불구하고 런타임에는 오류가 발생// 수정 전
interface ComponentProps {
  onSelectItem: (item: any) => void;
}
// 수정 후
interface ComponentProps {
  onSelectItem: (item: number) => void;
}
// 유지
function renderSelector(props: ComponentProps) {
  //...
}
let selectedId: number = 0;
function handleSelectItem(item: any) {
  selectedId = item.id;
}
renderSelector({ onSelectItem: handleSelectItem });타입 설계는 정확하고 명료한 코드 작성을 위해 필수이다.
또한 동료들과의 원활한 협업을 위해 필수이다.
any 타입을 쓰지 않으면, 런타임에 발견된 오류를 미리 잡아 신뢰도를 높일 수 있다.
어쩔 수 없이 any를 사용해야 하는 상황에서,
any의 단점을 보완하는 방법에 대해서 5장에서 배우자
🎯 요약
- any 타입을 사용하면 타입 체커와 타입스크립트 언어 서비스를 무력화시킨다.
any 타입은 진짜 문제점을 감추며, 개발 경험을 나쁘게 하고, 타입 시스템의 신뢰도를 떨어뜨린다.
✅ 질문
✅ 답
✅ 느낀점
아이템 5의 4번이 잘 안와닿는다.
모델링이 뭘까..?
1장이라 금방 끝날 줄 알았는데, 엄청 오래걸렸다.
정리를 어느정도 해야할지..감이 안잡힌다.
피드백 환영입니다!
✅ 참고자료
이펙티브 타입스크립트
아이템 1 & 3 컴파일 타임과 런타임과 에러 / 컴파일 타임과 런타임
아이템 2 추가 tsconfig.json 설정
아이템 2 추가 tsconfig.json 박영웅님
아이템 3 태그된 유니온
아이템 4 덕타이핑 vs 구조적 타이핑
개발자로서 성장하는 데 큰 도움이 된 글이었습니다. 감사합니다.