TypeScript 요약 필기

밀크티·2022년 4월 11일
0
post-thumbnail

강의 참고 자료

Why you should use TypeScript?

자바스크립트는 타입 시스템이 없는 동적 프로그래밍 언어(=약한 타입 언어)
자바 스크립트의 변수는 문자열, 숫자 불린등 여러 타입의 값을 가질 수 있음.
타입 스크립트는 그런 자바스크립트에 강한 타입 시스템을 적용해 에러를 컴파일 환경에서 코드를 입력하는 동안 체크 할 수 있게 함.

타입 스크립트 확장자: .ts
타입 스크립트 작성 후 컴파일러를 사용해 자바 스크립트 파일로 컴파일해 사용할 수 있음.

VSCode에는 타입스크립트 기능이 내장돼있어, 별도 설정 없이도 .ts파일을 인식할 수 있음.
단, 컴파일러는 비포함이라 별도 설치해야함.

npm install typescript

컴파일러 일반 지역 설치:

$ npm install -D typescript
$ npx tsc --version
$ npx tsc ./src/index.ts

개발 환경으로 Parcel 번들러 설치

$ mkdir typescript-test
$ cd typescript-test
$ npm init -y
$ npm install -D typescript parcel-bundler

tsconfig.json 파일, main.ts 파일과 index.html 파일을 생성

//index.html
<!doctype html>
<html>
<head>
  <meta charset="UTF-8">
  <title>TypeScript Test</title>
</head>
<body>
  <script src="main.ts"></script> // .js가 아닌 .ts 파일을 연결해야함.
</body>
</html>

마지막으로 터미널에 index.html을 지정하고 parcel 번들러로 빌드

$ npx parcel index.html
# Server running at http://localhost:1234

타입 Types

타입 지정

function add(a: number, b: number) {
  return a + b;
}
const sum: number = add(1, 2);
console.log(sum); // 3

오류를 검색하기 쉽게 띄워줌 ex) ts2322

타입선언:

📌Boolean
//ture/flase 값을 나타냄
let isBoolean: boolean;
let isDone: boolean = false;

📌Number
//소수점, 2진수, 8진수 리터럴까지 지원
let num: number;
let integer: number = 6;
let float: number = 3.14;
let hex: number = 0xf00d; // 61453
let binary: number = 0b1010; // 10
let octal: number = 0o744; // 484
let infinity: number = Infinity;
let nan: number = NaN;

📌String 
//따음표 ", ', ` 다 문제 없이 지원
let str: string;
let red: string = 'Red';
let green: string = "Green";
let myColor: string = `My color is ${red}.`;
let yourColor: string = 'Your color is' + green;

📌Array 배열
// 문자열만 가지는 배열
let fruits: string[] = ['Apple', 'Banana', 'Mango'];
// Or
let fruits: Array<string> = ['Apple', 'Banana', 'Mango'];

// 숫자만 가지는 배열
let oneToSeven: number[] = [1, 2, 3, 4, 5, 6, 7];
// Or
let oneToSeven: Array<number> = [1, 2, 3, 4, 5, 6, 7];

//문자와 숫자를 동시에 가지는 배열
let array: (string | number)[] = ['Apple', 1, 2, 'Banana', 'Mango', 3];
// Or
let array: Array<string | number> = ['Apple', 1, 2, 'Banana', 'Mango', 3

//배열이 가지는 항목을 단언할 수 없다면 any를 사용
let someArr: any[] = [0, 1, {}, [], 'str', false];

//인터페이스로 커스텀 파입을 사용
interface IUser {
  name: string,
  age: number,
  isValid: boolean
}
let userArr: IUser[] = [
  {
    name: 'Neo',
    age: 85,
    isValid: true
  },
  {
    name: 'Lewis',
    age: 52,
    isValid: false
  }
];

//별쓸모는 업시만 특정값으로 타입을 대신할 수도 았긴 있음
let array = 10[];
array = [10];
array.push(10);
array.push(11); // Error - TS2345

//타입 선언 바로 앞에 readonly 키워드 나 ReadonlyArray 타입을 사용할 해서 읽기 전용으로 만들 수도 있음
let arrA: readonly number[] = [1, 2, 3, 4];
let arrB: ReadonlyArray<number> = [0, 9, 8, 7];

arrA[0] = 123; // Error - TS2542: Index signature in type 'readonly number[]' only permits reading.
arrA.push(123); // Error - TS2339: Property 'push' does not exist on type 'readonly number[]'.

arrB[0] = 123; // Error - TS2542: Index signature in type 'readonly number[]' only permits reading.
arrB.push(123); // Error - TS2339: Property 'push' does not exist on type 'readonly number[]'.

튜플과 이넘
튜플: 길이와 타입이 고정된 배열. 타입스크립트에만 존재하고 자바에는 존재하지 않음.

이넘: 열거형

Any: 모든 타입
Unknown: 알 수 없는 타입, 언노운에는 어떤 타입의 값도 할당할 수 있지만, 다른 타입에 언노운을 할당할 수는 없다.

Null/Undefined: 모든 타입의 하위 타입으로 서로의 타입에도 할당 가능

Void: 값을 반환하지 않는 함수서 사용

function hello(msg: string): void { //: void의 위치는 함수가 반환 타입을 명시하는 곳
  console.log(`Hello ${msg}`);
}

Never: 절대 발생하지 않을 값, 어떤 타입도 적용할 수 없음.

Union: 2개 이상의 타입을 허용하는 경우, |를 사용해 타입을 구분하며 (괄호)는 선택 사항

let union: (string | number);
union = 'Hello type!';
union = 123;
union = false; // Error - TS2322: Type 'false' is not assignable to type 'string | number'.

인터섹션: &를 사용해 2개 이상의 타입을 조합하는 경우

// 기존 타입들이 조합 가능하다면 인터섹션을 활용할 수 있습니다.
interface IUser {
  name: string,
  age: number
}
interface IValidation {
  isValid: boolean
}
const neo: IUser & IValidation = {
  name: 'Neo',
  age: 85,
  isValid: true
};

함수: 화살표 함수를 이용해 타입을 지정, 인수의 타입과 반환값의 타입을 입력

// myFunc는 2개의 숫자 타입 인수를 가지고, 숫자 타입을 반환하는 함수.
let myFunc: (arg1: number, arg2: number) => number;
myFunc = function (x, y) {
  return x + y;
};

// 인수가 없고, 반환도 없는 경우.
let yourFunc: () => void;
yourFunc = function () {
  console.log('Hello world~');
};

타입추론

//예시. 변수에 숫자 12를 할당해 숫자 타입으로 추론되었기 떄문에 string 타입의 값을 할당할 수 없기애 발생한 에러
let num = 12;
num = 'Hello type!'; // TS2322: Type '"Hello type!"' is not assignable to type 'number'.
// 타입 가드
// 매번 타입 단언을 사용하기 보다는, NAME is TYPE 형태의 타입 술부를 반환 타입으로 명시한 함수를 이용함. 
//여기서 타입 술부는 val is number
function isNumber(val: string | number): val is number {
  return typeof val === 'number';
}

function someFunc(val: string | number) {
  if (isNumber(val)) {
    val.toFixed(2);
    isNaN(val);
  } else {
    val.split('');
    val.toUpperCase();
    val.length;
  }
}

인터페이스

//인터페이스에서는 :이나 , 등의 기호를 사용하지 않아도됨.
interface IUser {
  name: string,
  age: number
}
// Or
interface IUser {
  name: string;
  age: number;
}
// Or
interface IUser {
  name: string
  age: number
}

선택적 속성으로 정의하는 법: 물음표를 사용한다.

interface IUser {
  name: string,
  age: number,
  isAdult?: boolean // Optional property
}

// `isAdult`를 초기화하지 않아도 에러가 발생하지 않습니다.
let user: IUser = {
  name: 'Neo',
  age: 123
};

함수 타입

interface IUser {
  name: string
}
interface IGetUser {
  (name: string): IUser
}

// 매개 변수 이름이 인터페이스와 일치할 필요가 없습니다.
// 또한 타입 추론을 통해 매개 변수를 순서에 맞게 암시적 타입으로 제공할 수 있습니다.
const getUser: IGetUser = function (n) { // n is name: string
  // Find user logic..
  // ...
  return user;
};
getUser('Heropy');

인터페이스로 클래스르 정의하는 경우 implements를 사용

interface IUser {
  name: string,
  getName(): string
}

class User implements IUser {
  constructor(public name: string) {}
  getName() {
    return this.name;
  }
}

const neo = new User('Neo');
neo.getName(); // Neo
interface ICat {
  name: string
}

class Cat implements ICat {
  constructor(public name: string) {}
}

function makeKitten(c: ICat, n: string) {
  return new c(n); // Error - TS2351: This expression is not constructable. Type 'ICat' has no construct signatures.
}
const kitten = makeKitten(Cat, 'Lucy');
console.log(kitten);
//ICat은 호출 가능한 구조가 아님.
//따라서 오류를 고치기 위해서는
//대신 new 키워드를 사용하고 ICatConstructor라는 호출 가능한 인터페이스를 정의
interface ICat {
  name: string
}
interface ICatConstructor {
  new (name: string): ICat;
}

class Cat implements ICat {
  constructor(public name: string) {}
}

function makeKitten(c: ICatConstructor, n: string) {
  return new c(n); // ok
}
const kitten = makeKitten(Cat, 'Lucy');
console.log(kitten);

인덱싱 기능 타입

interface IItem {
  [itemIndex: number]: string | boolean | number[]
}
let item: IItem = ['Hello', false, [1, 2, 3]];
console.log(item[0]); // Hello
console.log(item[1]); // false
console.log(item[2]); // [1, 2, 3]

인터페이스도 클래스의 implements처럼 extend 키워드를 활용해 상속할 수 있음.

interface IAnimal {
  name: string
}
interface ICat extends IAnimal {
  meow(): string
}

타입별칭
조합한 타입뜰을 참조하는 별칭을 만드는 것

type MyType = string;
type YourType = string | number | boolean;
type TUser = {
  name: string,
  age: number,
  isValid: boolean
} | [string, number, boolean];

let userA: TUser = {
  name: 'Neo',
  age: 85,
  isValid: true
};
let userB: TUser = ['Evan', 36, false];

function someFunc(arg: MyType): YourType {
  switch (arg) {
    case 's':
      return arg.toString(); // string
    case 'n':
      return parseInt(arg); // number
    default:
      return true; // boolean
  }
}

제네릭

//T는 타입변수고 타입변수는 매개 변수처럼 원하는 이름으로 지정할 수 있음.
function toArray<T>(a: T, b: T): T[] {
  return [a, b];
}

toArray<number>(1, 2);
toArray<string>('1', '2');
toArray<string | number>(1, '2');
toArray<number>(1, '2'); // Error
// 타입변수 T가 string과 number인 경우만 허용해 extend를 사용한 제약 조건을 추가
T extends U

조건부 타입의 기본 문법

💡 조건부 타입(conditional types)
//extend 키워드
T extends U ? X : Y
//T 타입이 U에 할당가능한 타입이면 X라는 타입이고, 아니면 Y라는 타입

//infer 키워드
T extends infer U ? X : Y
// u가 추론 가능한 타입이면 참이고 아니면 거짓

this

const obj = {
  a: 'Hello~',
  b: function () {
    console.log(this.a); // obj.a
    // Inner function
    function b() {
      console.log(this.a); // global.a
    }
  }
};
//문제상황
obj.b(); // Hello~

const b = obj.b;
b(); // Cannot read property 'a' of undefined

function someFn(cb: any) {
  cb();
}
someFn(obj.b); // Cannot read property 'a' of undefined

setTimeout(obj.b, 100); // undefined

//해결 1: bind 메소드
obj.b(); // Hello~

const b = obj.b.bind(obj);
b(); // Hello~

function someFn(cb: any) {
  cb();
}
someFn(obj.b.bind(obj)); // Hello~

setTimeout(obj.b.bind(obj), 100); // Hello~

// 해결 2: 화살표 함수
obj.b(); // Hello~

const b = () => obj.b();
b(); // Hello~

function someFn(cb: any) {
  cb();
}
someFn(() => obj.b()); // Hello~

setTimeout(() => obj.b(), 100); // Hello~

오버로드
이름은 같지만 매개변수 타입과 반환 타입이 다른 여러 함수를 가질 수 있는 것

function add(a: string, b: string): string; // 함수 선언
function add(a: number, b: number): number; // 함수 선언
function add(a: any, b: any): any { // 함수 구현
  return a + b;
}

add('hello ', 'world~');
add(1, 2);
add('hello ', 2); // Error - No overload matches this call.

클래스

클래스 바디란 {중괄호}로 묶여 있는 영역을 의미
클래스의 생성자 매소드constructor가 일반 메소드 멤버 class memebr과는 다르게 속성은 name: string; 아 같이 클래스 바디에 별도로 타입을 선언함.

class Animal {
  name: string;
  constructor(name: string) {
    this.name = name;
  }
}
class Cat extends Animal {
  getName(): string {
    return `Cat name is ${this.name}.`;
  }
}
let cat: Cat;
cat = new Cat('Lucy');
console.log(cat.getName()); // Cat name is Lucy.

클래스 수식어

정적으로 사용되는 static과 읽기전용인 readonly는 위의 접근 제어자와 함께 사용할 수 있음.

추상클래스
타클래스가 파생될 수 있는 기본 클래스로 인터페이스와 유사

// Abstract Class
abstract class Animal {
  abstract name: string; // 파생된 클래스에서 구현해야 합니다.
  abstract getName(): string; // 파생된 클래스에서 구현해야 합니다.
}
class Cat extends Animal {
  constructor(public name: string) {
    super();
  }
  getName() {
    return this.name;
  }
}
new Animal(); // Error - TS2511: Cannot create an instance of an abstract class.
const cat = new Cat('Lucy');
console.log(cat.getName()); // Lucy

// Interface
interface IAnimal {
  name: string;
  getName(): string;
}
class Dog implements IAnimal {
  constructor(public name: string) {}
  getName() {
    return this.name;
  }
}
//추상 클래스와 인터페이스의 차이점은 속성, 메소드 멤버에 대한 세부 구현 가능여부임. 추상클래스는 가능함. 인터페이스는 불가능

매개변수

function add(x: number, y?: number): number {
  return x + (y || 0);
}
const sum = add(2);
console.log(sum);
// ? 키워드의 사용은 | undefined를 추가 하는 것과 같음
function add(x: number, y: number | undefined): number {
  return x + (y || 0);
}
const sum = add(2, undefined);
console.log(sum);

속성과 메소드

interface IUser {
  name: string,
  age: number,
  isAdult?: boolean   //isAdult를 선택적 속성으로 선언함
}

let user1: IUser = {
  name: 'Neo',
  age: 123,
  isAdult: true
};

let user2: IUser = {
  name: 'Evan',
  age: 456
};```

페이닝: [참고자료](https://developer.mozilla.org/ko/docs/Web/JavaScript/Reference/Operators/Optional_chaining)

//str 속성이 undefined일 경우 toString 메소드를 사용 할 수 없어서 에러 발생.
obj?.prop;
obj?.[expr];
arr?.[index];
func?.(args);

// Error - TS2532: Object is possibly 'undefined'.
function toString(str: string | undefined) {
return str.toString();
}

// Type Assertion, str속성이 문자열이라는 것을 단언한 문제 해결법
function toString(str: string | undefined) {
return (str as string).toString();
}

// Optional Chaining ?.로 문제 해결
function toString(str: string | undefined) {
return str?.toString();
}

0개의 댓글