[TS] TypeScript 타입

seung·2022년 4월 29일
0

[MGS] 온라인 강의

목록 보기
5/5

타입 종류


변수의 타입을 지정하고 싶을 때는 변수명 뒤에 콜론을 표시한 뒤 타입을 입력한다.

let 변수명: 타입;


✨ number


정수(int), 실수(float) 구분없이 모두 number로 표시한다.

let age: number = 20;

✨ string


문자열을 나타낸다.

let name: string = 'seung';

✨ boolean


참과 거짓 값을 나타낸다.

let	isAdult: boolean = true;

✨ Symbole


Symbol은 고유하고 수정불가능한 값으로 만들어줌

주로 접근 제어하는데 쓰는 경우가 많았음

  • ECMAScript 2015의 Symbol
  • new Symbol 로 사용 X
  • Symbol을 함수로 사용해서 symbol 타입을 만들어낼 수 있음
  • tsconfig.json에서 lib 옵션에 “ES2015”, “DOM” 을 추가하면 사용 가능
const sym = Symbol();

const obj = {
  [sym]: "value",
};

obj[sym];

✨ object


primitive type 이 아닌 것을 나타내고 싶을 때 사용하는 타입

→ not number, string, boolean, bigint, symbol, null or undefined

let obj: object = {};
obj = { name: 'Mark' };
obj = [{ name: 'Mark' }];

✨ array


Array<타입> 또는 타입[] 형태로 사용한다.

Array<타입> 형태는 jsf나 tsx에서 충돌이 날 수 있으니 사용을 자제

const list: number[] = [1, 2, 3];
const list2: Array<number> = [1, 2, 3];

✨ tuple


배열과 유사하지만 타입과 길이가 고정되어 있다는 점이 다르다.

let tuple: [number, string];
tuple = [1, 'a'];

순서대로 지정된 타입을 입력하지 않으면 에러 발생한다.

tuple = ['b', 3]; // 💥 ERROR

고정된 길이의 배열이지만 push가 가능하긴 하다.

tuple.push('c'); // tuple = [2, 'a', 'c']

✨ enum


enum Role { ADMIN, READ_ONLY, AUTHOR }

✨ any


어떠한 값이든 들어올 수 있고, any는 최대한 사용하지 않는 것을 권장한다.

왜냐하면 컴파일 타임에 타입 체크가 정상적으로 이루어지지 않기 때문이다.

function returnAny(message): any {
  console.log(message);
}
  • nolmplicitAny 옵션
    - 타입을 명시적으로 지정하지 않은 경우, 타입스크립트가 추론 중 any 라고 판단하게 되면 컴파일 에러를 발생시켜 명시적으로 지정하도록 유도한다.

✨ unknown


아무 값이나 지정할 수 있다는 점은 any와 유사하다.

차이점은 any타입을 제외한 다른 타입으로 선언한 변수에 할당할 수 없어서 타입을 먼저 확인한 후에 작업을 할 수 있다.

→ 컴파일러가 타입을 추론할 수 있게끔 타입의 유형을 좁힘

→ 타입을 확정해주지 않으면 다른 곳에 할당할 수 없고, 사용할 수 없음

let userInput: unknown;
let userName: string;

userInput = 5;
userInput = 'Seung';

// userName = userInput; // 💥 ERROR. userInput의 타입을 정확히 체크해야 함.

if (typeof userInput === 'string') {
	userName = userInput;
}

✨ void


값을 반환하지 않는 함수를 사용하는 경우 void를 표준으로 사용한다.

함수가 의도적으로 아무것도 반환하지 않는다면 void 사용해야 한다.

function printResult(str: string): void {
	console.log(`Result: ${str}`);
}

✨ undefined


위의 printResult 함수와 마찬가지로 아무것도 반환하지 않는 것처럼 유사하다고 생각하겠지만,

return; 을 해줌으로써 반환타입도 undefined가 된다.

function getUndefined(str: string): undefined {
	console.log(`Result: ${str}`);
	return;
}
  • strictNullChecks 옵션
    - 모든 타입에 자동으로 포함되어 있는 null 과 undefined를 제거해준다.

✨ Function


해당 변수가 Function 타입이라고 지정할 수 있다.

function add(n1: number, n2: number): number {
	return n1 + n2;
}

let combineValues: Function;
combineValues = add;
// combineValues = 5; // 💥 ERROR. 타입이 Function이 아님
combineValues = printResult; // 지정은 되지만 내가 원하는 함수가 아님

하지만, 다른 함수를 지정해도 타입이 Function이기 때문에 변수에 할당이 되는 단점이 있다.

그 점을 해결하기 위해서 화살표함수를 이용해 매개변수 및 반환 타입을 지정해준다.

let combineValues: (a: number, b: number) => number;
combineValues = add;
// combineValues = printResult; // 💥 ERROR. 타입이 맞지 않음
  • strictFunctionTypes 옵션
    • 함수를 할당할 시에 함수의 매개변수 타입이 같거나 슈퍼타입인 경우가 아닌 경우, 에러를 통해 경고한다.
class Person() {}
class Developer extends Person {
  coding() {};
}
<class StartupDeveloper extends Developer {
  burning() {};
}

function tellme(f: (d: Developer) => Developer) {}

function tellme(function dToD(d: Developer): Developer) {
  return new Developer();
});

function tellme(function pToD(d: Person): Developer) {
  return new Developer();
});

// 💥 StartupDeveloper가 Developer보다 하위 클래스이므로 논리에 맞지 않다.
//    → strictFunctionTypes 옵션을 켜주면 에러 발생 시킨다.
function tellme(function dToD(d: StartupDeveloper): Developer) {
  return new Developer();
});

✨ never


에러 발생시키는 함수처럼 아무것도 반환하지 않고 기본적으로 스크립트나 스크립트의 일부를 충돌시키거나 망가뜨리기 위한 함수의 반환값으로 사용가능하다.

never는 모든 타입에 할당할 수 있지만, never에는 그 어떤 것도 할당할 수 없다.

any 조차도 never에게 할당할 수 없다.

그래서 잘못된 타입을 넣는 실수를 막고자 할 때 사용하기도 한다.

function generateError(message: string, code: number): never {
  throw { message: message, errorCode: code };
}

generateError('An error occurred!', 500);

🔥 union


2개 이상의 타입을 허용하는 경우이며, | (vertical bar) 를 이용해 타입을 구분한다.

function combine(
	input1: number | string
	input2: number | string
) {
	// ...생략
}

🔥 literal


특정한 값을 지정하여 해당 값만 허용하도록 한다.

string뿐만 아니라 number 등 다른 타입들 가능.

function combine(
	input1: number | string,
	input2: number | string,
	// 'as-number' 또는 'as-text'라고 명시된 문자열만 입력가능
	resultConversion: 'as-number' | 'as-text'
) {
	// ...생략
}

🔥 type alias (사용자 지정 타입)


사용자가 타입을 변수로 저장하여 사용할 수 있다.

type Combinable = number | string;
// 'as-number' 또는 'as-text'라고 명시된 문자열만 입력가능
type ConversionDescription = 'as-number' | 'as-text';

function combine(
	input1: Combinable,
	input2: Combinable,
	resultConversion: ConversionDescription
) {
	// ...생략
}

type User = { name: string; age: number };
 
function greet(user: User) {
  console.log('Hi, I am ' + user.name);
}

🔥 함수 반환타입


함수의 매개변수 지정한 괄호 뒤에 : 타입명을 입력하여 지정할 수 있다.

function add(num1: number, num2: number): number {
	return num1 + num2;
}

🔥 콜백 함수


콜백 함수의 매개변수 및 반환 타입도 지정할 수 있다.

콜백 함수는 자신이 전달되는 인수가 반환 값을 기대하지 않는 경우에도 값을 반환할 수 있다.

function sendRequest(data: string, cb: (response: any) => void) {
  // ... sending a request with "data"
  return cb({data: 'Hi there!'});
}
 
sendRequest('Send this!', (response) => { 
	console.log(response);
	return true; // ❗️ 반환값은 void이지만 return 가능
});

고급타입


intersection types

여러 타입을 합쳐서 사용하는 타입으로, &를 통해 타입을 결합할 수 있다.

👉 예제 1

아래 예제는 객체를 결합하여 두 타입에 있는 모든 프로퍼티를 사용해야 한다.

type Admin = {
  name: string;
  privileges: string[];
};

type Employee = {
  name: string;
  startDate: Date;
};

type ElevatedEmployee = Admin & Employee;

const e1: ElevatedEmployee = {
  name: 'seung',
  privileges: ['create-server'],
  startDate: new Date();
}

👉 예제 2

위의 예제와 다르게 union type으로 이루어진 두 타입을 결합했을 때는 공통의 타입만 사용 가능하다.

type Combinable = string | number;
type  Numeric = number | boolean;

type Universal = Combinable & Numeric; // ❗️ number 타입만 가능

타입 가드 (type guard)

런타임 시 특정 타입으로 작업을 수행하기 전에 해당 타입을 검사하는 코드 패턴

여러 타입을 가지는 유니온 타입일 때, 특정 타입일 시 조건을 두는 라인을 타입 가드라고 한다.

👉 예제 1

단순 타입일 때는 typeof를 통해 확인한다.

function add(a: Combinable, b: Combinable) {
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
  return a + b;
}

👉 예제 2

객체 타입의 속성에 접근하는 타입 가드는 in을 통해 해당 속성 유무를 확인 가능하다.

type UnknownEmployee = Employee | Admin;

function printEmployeeInformation(emp: UnknownEmployee) {
  console.log(`Name: ${emp.name}`);
  
  if ('privileges' in emp) {
    console.log(`Privileges: ${emp.privileges}`);
  }
	if ('startDate' in emp) {
    console.log(`Privileges: ${emp.startDate}`);
  }
}

👉 예제 3

클래스 타입instaceof를 통해 확인한다.

class Car {
  drive() {
    console.log('Driving...');
  }
}

class Truck {
  drive() {
    console.log('Driving a truck...');
  }
  loadCargo(amount: number) {
    console.log(`Loading cargo... ${amount}`);
  }
}

type Vehicle = Car | Truck;

function useVehicle(vehicle: Vehicle) {
  vehicle.drive();
  
  if (vehicle instanceof Truck) {
    vehicle.loadCargo(1000);
  }
}

👉 예제 4

인터페이스 타입type 프로퍼티를 추가해 switch문으로 구분하는 방법을 사용할 수 있다.

 interface Bird {
  type: 'bird';
  flyingSpeed: number;
}

interface Horse {
  type: 'horse';
  runningSpeed: number;
}

type Animal = Bird | Horse;

function moveAnimal(animal: Animal) {
  let speed;
  switch (animal.type) {
    case 'bird':
      speed = animal.flyingSpeed;
      break;
    case 'horse':
      speed = animal.runningSpeed;
  }
  console.log(`Moving at speed: ${speed}`);
}

형변환


타입스크립트에 특정 값이 특정 타입이라고 알릴 때 유용

요소 앞에 추가

<타입명>을 적어 요소 앞에 추가한다.

const userInputEl = <HTMLInputElement>document.querySelector('#user-input');
userInputEl.value = 'Hi there';

뒤에 추가

! as 타입명을 요소 뒤에 추가한다.

const userInputEl = document.querySelector('#user-input')! as HTMLInputElement;
userInputEl.value = 'Hi there';

인덱스


유연성을 제공하기 위해 프로퍼티 이름과 필요한 프로퍼티의 개수를 지정할 필요가 없다.

프로퍼티 식별자에 []를 감싸서 프로퍼티 식별자 이름의 타입을 지정한다 ⇒ 지정 타입 내 아무이름이나 가능

❗️ 프로퍼티 식별자 이름의 타입은 boolean 사용 X

interface ErrorContainer {
  // id: number; // 💥 인덱스 타입으로 모든 프로퍼티를 string으로 지정했기 때문에 불가능
  [prop: string]: string;
}

const errorBag: ErrorContainer = {
  email: 'Not a vaild email',
  username: 'Must start with a capital character!',
  1995: 'number test', // ✨ 프로퍼티 식별자에 숫자를 입력하도 문자열로 해석됨 => '1995'
};

함수 오버로드


자체적으로 반환 타입을 정확히 추론하지 못하는 경우에 유용하다.

함수 선언 코드 위에 매개변수 및 반환타입을 다르게 지정하여 적어준다.

function add(a: number, b: number): number;
function add(a: string, b: string): string;
function add(a: Combinable, b: Combinable) {
  if (typeof a === 'string' || typeof b === 'string') {
    return a.toString() + b.toString();
  }
  return a + b;
}

const result = add('kim', ' seung won');
// ❗️ 매개변수 타입이 Combinable일 때는 반환값이 number | string 이므로 
// string만 쓸 수 있는 split() 함수 사용 못함
result.split(' ');

선택적 체이닝 (optional chaining)


정의되어 있는지 여부가 확실하지 않은 요소 다음에 ?를 추가한다.

❗️ 타입스크립트 3.7 버전 이상에서만 지원

❗️ IDE에서는 컴파일 에러가 뜨지만 구문은 지원함

const fetchedUserData = {
  id: 'u1',
  name: 'Max',
  // job: { title: 'CEO', description: 'My own company' },
};
console.log(fetchedUserData?.job?.title);

null 병합


null 또는 undefined일 때만 ?? 뒤의 값을 할당한다.

❗️ 빈 문자열(’’)이나 0은 앞의 값을 할당

const storedData = userInput ?? 'DEFAULT';
profile
🌸 좋은 코드를 작성하고 싶은 프론트엔드 개발자 ✨

0개의 댓글