[TS] Section2 기본 & 타입

lim1313·2022년 1월 17일
0

TIL

목록 보기
7/22

🍉 타입 추론 Type Inference

타입스크립트에서는 타입 표기가 없는 경우, 타입 정보를 제공하기 위해서 타입을 유추한다.

let a = 5;
a = "hello"; // => error
let a = 5;
a = 40; // => 정상 동작 

🍉 타입 단언 Type Assertions

타입스크립트가 타입 추론을 통해 판단할 수 있는 타입의 범주를 넘는 경우, 더 이상 추론하지 않도록 지시할 수 있다.
이를 ‘타입 단언’이라고 하며, 이는 프로그래머가 타입스크립트보다 타입에 대해 더 잘 이해하고 있는 상황을 의미한다.

타입스크립트는 ‘isNumber’라는 이름만으로 위 내용을 추론할 수 없기 때문에 “val이 문자열인 경우 toFixed를 사용할 수 없다”고 (컴파일 단계에서) 다음과 같은 에러를 반환한다.

function someFunc(val: string | number, isNumber: boolean) {
  // some logics
  if (isNumber) {
    val.toFixed(2); // Error - TS2339: ... Property 'toFixed' does not exist on type 'string'.
  }
}

따라서 우리는 isNumber가 true일 때 val이 숫자임을 다음과 같이 단언할 수 있다.

function someFunc(val: string | number, isNumber: boolean) {
  // some logics
  if (isNumber) {
    // 1. 변수 as 타입
    (val as number).toFixed(2);
    // Or
    // 2. <타입>변수
    // (<number>val).toFixed(2);
  }
}

📌 논리형(Boolean)

let bool01: boolean = true;
let bool02: boolean = false;

📌숫자형(Number)

let num01: number = 5;
let num02: number = 3.14;
let num03: number = NaN;

📌문자열(String)

ES6의 템플릿 문자열도 지원한다.

let str01: string = 'text';
let str02: string = `my name is ${val}`;

📌 Object 객체

const person: {
  name: string;
  age: number;
} = {
  name: 'max',
  age: 30,
};

console.log(person.name);

물론 객체 타입은 중첩 객체에 대해서도 생성할 수 있다.

const product = {
  id: 'abc1',
  price: 12.99,
  tags: ['great-offer', 'hot-and-new'],
  details: {
    title: 'Red Carpet',
    description: 'A great carpet - almost brand-new!'
  }
}

//이러한 객체의 타입은 아래와 같다.

{
  id: string;
  price: number;
  tags: string[];
  details: {
    title: string;
    description: string;
  }
}

따라서 객체 타입 안에 객체 타입이 있다고 말할 수 있다.

📌 Array 배열

let favoritesports: string[];
favoritesports = ['sports', 'running'];

let favoritesports2: any[];
favoritesports2 = ['sports', 1, 3, true, { name: 'ye' }];

let favoritesports3: (string | number)[];
favoritesports3 = ['sports', 1, 3];
const person2 = {
  name: 'max',
  age: 30,
  hobbies: ['sports', 'cooking', 3],
};

for (const hobby of person2.hobbies) {
  console.log(hobby.toUpperCase()); 
//=> 타입 추론으로 number 타입이 있기 때문에 toUpperCase error가 발생
}

📌 tuple 튜플

배열 안의 요소가 원하는 길이만큼 원하는 타입을 지정할 수 있다.

const person1: {
  hobbies: string[];
  role: [number, string]; // tuple : 배열 안의 요소가 원하는 길이만큼 원하는 타입을 지정
} = {
  hobbies: ['sports', 'cooking'],
  role: [10, 'tuple'],
};

person1.role.push('admin'); // => 정상
person1.role[1] = 10; // => Error
person1.role = [0, 'dd', 'dd']; // => Error

let tuple: [string, number];
tuple = ['hello', 10]; // OK
tuple.push(true); // Error
  • 배열의 각 요소의 타입을 지정할 수 있다.
  • 해당 배열의 길이가 지정된다.
  • 하지만, push 메서드를 사용하면 배열의 길이가 길어질 수 있다.

📌 enum 이넘 열거형

특정 값들의 집합을 의미하는 자료형
문자형 이넘과 숫자형 이넘을 지원

enum Role {ADMIN = 'admin', READ = 12, AUTHOR}
const person = {
  name: 'max',
  age: 30,
  hobbies: ['sports', 'cooking'],
  role: Role.ADMIN,
};

if (person.role === Role.ADMIN) {
  console.log(Role.ADMIN);
}

enum Color1 {Red, Green, Blue};
let c1: Color1 = Color1.Green;

console.log(c1); // 1

type Product = {title: string; price: number;}
const p1: Product = { title: 'A Book', price: 12.99, isListed: true } 
// isListed는 Product 타입에 포함되지 않아 컴파일 에러가 발생한다.
//=> ERROR

📌 any 모든

모든 타입의 value를 허용한다.

타입 추론(type inference)할 수 없거나 타입 체크가 필요 없는 변수에 사용한다.

하지만 any를 쓸 경우 Typescript 컴파일러가 아무것도 점검할 수 없기 때문에, 타입스크립트를 쓰는 장점이 사라지게 된다.

  let favoritesports2: any[];
  favoritesports2 = ['sports', 1, 3, true, { name: 'ye' }];
  
  let favoritesports22: any;
  favoritesports22 = { name: 'ye' };

📌 union 유니온

자바스크립트의 OR 연산자(||)와 같이 A이거나 B이다 라는 의미의 타입

function combine(input1: number | string, input2: number | string) {
  //타입에 따른 런타임 확인이 필요한 경우 다음과 같은 조건 분기를 할 수 있다.
  let result;
  if (typeof input1 === 'number' && typeof input2 === 'number') {
    result = input1 + input2;
  } else {
    result = input1.toString() + input2.toString();
  }
  return result;
}

const combineAges = combine(30, 26);
console.log(combineAges);

const combineNames = combine('max', 'anna');
console.log(combineAges);

📌 리터럴 타입

문자열과 숫자, boolean 세 가지 리터럴 타입이 있는데 이를 사용하면 문자열이나 숫자에 정확한 값을 지정할 수 있다.

 function combine2(
    input1: number | string,
    input2: number | string,
    resultConversion: 'as-number' | 'as-text'
  ) {
    let result;
    if (
      (typeof input1 === 'number' && typeof input2 === 'number') ||
      resultConversion === 'as-number'
    ) {
      result = +input1 + +input2;
    } else {
      result = input1.toString() + input2.toString();
    }
    return result.toString();
  }

  const combineNames = combine2('max', 'anna', 'as-text');
  console.log(combineNames); // => maxanna

  const combineStingAges = combine2('30', '26', 'as-number');
  console.log(combineStingAges); //=> 56

📌 타입 별칭 type alias

특정 타입이나 인터페이스를 참조할 수 있는 타입 변수를 의미

type MyName = string;
const name: MyName = 'capt';
type Combinable = number | string;
type ConversionDescriptor = 'as-number' | 'as-text';

function combine3(
  input1: Combinable,
  input2: Combinable,
  resultConversion: ConversionDescriptor
){ ... }

타입 별칭을 사용하여 타입을 직접 “생성”할 수 있다. 유니온 타입을 저장하는 것만 가능한 것이 아니다. 복잡할 수 있는 객체 타입에도 별칭을 붙일 수 있다.

type User = { name: string; age: number };
const u1: User = { name: 'Max', age: 30 }; // this works!

📌 함수 반환 타입 및 무효 void

//TODO return 타입 설정
function add(n1: number, n2: number): number {
  return n1 + n2;
}

//TODO return 타입 : void / 아무런 return이 없을 때
// functin에서 아무런 return이 없을 때 undefied 타입이 불가능하다
// undefined 대신 void를 사용한다
function printResult(num: number): void {
  console.log('result' + num);
}

function printResult2(num: number): void {
  console.log('result' + num);
  return;
}

// undefined를 return할 때 undefined 타입을 사용할 수는 있지만,
// 거의 사용하지 않는다.
function printResult3(num: number): undefined {
  console.log('result' + num);
  return;
}

printResult(add(5, 12));

// undefined 타입은 존재한다
let someValue: undefined;

📌 함수 타입

//TODO 함수 타입
let combineValues: Function;

//TODO 두개의 number 타입 매개변수를 받아 number 타입을 반환한다
let combineValues2: (a: number, b: number) => number;

console.log(combineValues(8, 8));

📌 함수 타입 및 콜백

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

//TODO 함수 타입 및 콜백
function addAndHandle(n1: number, n2: number, cb: (num: number) => number) {
  const result = n1 + n2;
  cb(result);
}

addAndHandle(10, 20, (result) => {
  console.log(result);
  return 3;
});

📌 unknown 타입

사용자가 어떤 값을 입력할지 모를 경우 모든 타입을 사용할 수 있다.

unknown vs any

  • unknown
    : any 타입을 제외한 다른 타입으로 선언한 변수에 할당할 수 없다
    : if문으로 타입을 설정한다면 error가 발생하지 않고 할당이 가능하다.
    : 때문에 타입을 특정할 수 없지만, 특정 타입 조건에 대해서 확인이 필요하다면 any보다는 unknown 타입을 사용하는 것이 좋다.

  • any
    : 모든 타입으로 선언한 변수에 할당할 수 있다.

//TODO numknown 타입
let userInput: unknown;
let userInput2: any;
let userInput3: string;

userInput = 5;
userInput = 'max';

// userInput3 = userInput; //=> error
userInput2 = userInput; // any 타입에는 할당할 수 있다.
userInput3 = userInput2; //=> 정상
userInput2 = userInput3; //=> 정상

// if문으로 타입을 설정한다면 error가 발생하지 않고 할당이 가능하다.
// 때문에 타입을 특정할 수 없지만, 특정 타입 조건에 대해서 확인이 필요하다면 any보다는 unknown 타입을 사용하는 것이 좋다.
if (typeof userInput === 'string') {
  userInput3 = userInput;
}

📌 never 타입

never는 절대 발생하지 않는 값의 타입을 표현하는데 사용하며 보통 에러를 발생하거나, 절대 return하지 않는(무한 루프) 함수의 return type으로 많이 사용한다.

function error(message: string): never {
  throw new Error(message);
}

function infiniteLoop(): never {
  while (true) {}
}

function fail() { // return type이 never으로 추론됨
  return error("Something failed");
}

never 타입은 모든 타입의 서브 타입이며, 어떤 타입도 ‘never’에 assign되지 않는다.

let any: any;
let bool: boolean = true;
let nullVal: null = null;
let undefinedVal: undefined = undefined;

let never1: never = any; // Type 'any' is not assignable to type 'never'.
let never4: never = nullVal; // Type 'null' is not assignable to type 'never'.
let never5: never = undefinedVal; // Type 'undefined' is not assignable to type 'never'

never vs void

never는 함수가 종료하지 않아 결코 return하지 않을 때 사용된다. 무한루프 혹은 Error 메시지를 throw할 때 return type이 never이다.
void는 return 값이 없을 뿐이지 함수는 종료한다.


interface vs type

typescript 문서
Interface vs Type alias in TypeScript 2.7

profile
start coding

0개의 댓글