Typescript (1) - Types

김정욱·2022년 1월 18일
0

Typescript

목록 보기
1/1
post-thumbnail

Type 종류

[ Function ]

  • 함수를 나타내는 function 타입에 대한 typescript인터페이스
  • 화살표 함수를 사용하는 방식이 더 깔끔
/* basic */
let a: Function // 함수 이외의 값을 할당하면 오류 발생
a = function (): void {
  console.log("Function Type")
}
/* arrow function */
let a: (str: string) => string
a = function (str: string): string {
  return str
}

[ Symbol ]

  • 개념
    • 불변하고, 유일한 값을 가지는 symbol 타입에 대한 typescript 인터페이스
  • 특징
    • objectkey값으로 사용
    • Object.keys()for.in 등과 같은 메소드로 접근되지 않기 때문에, hidden 속성(property)으로 사용 가능
  • 등장 이유
    • objectkey값이 문자열이나 정수형이라면 변경될 위험이 있지만,
      symbol불변하고, 유일하기 때문에 변경될 일이 없어서 위험이 없다
      (object같은 이름을 가지는 프로퍼티 혹은 메서드를 추가하면 덮어씌워질 수 있기 때문)
/* Symbol 생성자를 호출함으로써 생성 */
let sym2 = Symbol("key");
let sym3 = Symbol("key");
sym2 === sym3; // false, 심벌은 유일하다.

/* Symbol 레지스트리에 존재하는 심볼을 찾고 존재할 경우 이를 반환 */
console.log(Symbol.for('key') === Symbol.for('key')); // true
console.log(Symbol('key') === Symbol('key')); // false

/* 객체의 key로 활용 */
const sym = Symbol();
let obj = { 
 [sym]: "value",
};
console.log(obj[sym]); // "value"

[ any / unknown ]

  • any
    • 무엇이든 허용 / 항상 타입 검사를 만족
    • 타입을 지정하지 않아도(없어도) 컴파일 오류 발생 X
    • 가장 유용하지만, 가장 주의해서 사용해야 한다
  • unknwon
    • 타입을 사용하는 사람이 타입을 지정했는지 확인
    • 타입이 없거나 지정돼있지 않으면 컴파일 오류 발생 O
/* any */
let myVar: any = 42
myVar.toFixed() // number은 toFixed() 가 없지만 오류가 발생하지 않음

/* unknown */
let myVar: unknown = 'hello'
myVar = 42 // 어떤 타입이든 재할당 가능
let age: number = (myVar as number) // 반드시 사용하려면 타입 명시를 해줘야 한다

[ never ]

  • 개념
    • 절대 발생할 수 없는 타입을 의미
    • 모든 타입의 하위 타입을 의미
      -> 어떤 다른 값도 never 타입에 할당 불가
  • 사용 용도
    • 어떤 함수가 어떠한 값도 반환하면 안될 때 사용
    • 항상 예외를 발생시키는 함수일 경우 사용
/* 항상 예외를 발생시키는 함수 */
const fetchFriendsOfUser = (username: string): never => {
  throw new Error('Not Implemented');
}

/* 특정 타입 값을 할당받지 않도록 설정
   => string을 제외한 타입만 지정 가능 */
type NonString<T> = T extends string ? never : T;

[ void ]

  • 리턴 값이 없는 함수를 나타내며, 내부적으로는 undefined를 반환
  • 함수 내부에서 return 문이 존재하지 않을 때 적용
let tf: (supply: string) => void
tf = function(supply: string) => {
  console.log(supply)
}

[ enum ]

  • 열거형 데이터 타입
  • 값을 할당하지 않으면 차례대로 0부터 증가되며 숫자가 부여됨
enum Color {
  Red = 'RED',
  Yellow = 'YELLOW',
  Green = 'GREEN',
  Blud = 'BLUE'
}
let color = Color.Red // color은 Color 타입을 가짐

[ object ]

  • 객체를 나타내는 타입
let Dom: {
  version: string,
  el: () => void,
  css: () => void
};

Dom = {
  version: '0.0.1',
  el(){},
  css(){}
};

유니언 (Union) / 교차 (Intersection) 타입

  • 유니언 (Union) 타입
    • 여러 타입 중 하나일 수 있음을 선언하는 바법
    • 합 타입이라고 부르기도 함
    • 타입을 확인하기 위해서 typeof / instanceof 사용
function setInfo(id:number|string, name:string){
  return { id, name };
}

let myVar = number | string | object
  • 교차 (Intersection) 타입
    • 여러 타입을 하나로 결합한 타입
    • 곱 타입이라고 부르기도 함
interface ErrorHandling{
  success:boolean;
  error?:{message:string};
}
interface ArtworksData{
  artworks:{title:string}[];
}
interface ArtistsData{
  artists:{name:string}[];
}
//이 인터페이스들은
//하나의 에러 핸들링과 자체 데이터로 구성된다.

type ArtworkResponse=ArtworksData & ErrorHandling;
type ArtistsResponse=ArtistsData & ErrorHandling;

const handleArtistsResponse=(response:ArtistsResponse) =>{
  if(response.error){
    console.error(response.error.message);
    return;
  }
  console.log(response.artists);
};

[ String Literal Types ]

  • typescript에서 string typestring literal type은 구분
  • const로 선언된 변수는 변경될 일이 없기에 string이 아닌 string literal type으로 간주
/* string vs string literal */
const a = "Hello World" // string literal 
let b = "Hello World" // string 
const c: string = "Hello World" // string

/* 유용한 사용
    => 정적인 string literal 로 선언하면 enum과 같이 사용할 수 있음
      그리고, 에디터에서도 suggest가 된다 (컴파일 시점에 예외발생 + 오타 감소) */
type EventType = "mouseout" | "mouseover" | "click"
function handleEvent(event: EventType) {}
handleEvent("click")
  • Typescript에서는 string으로 object를 접근하면 오류가 발생한다 ?
    • 기본적으로 TS에서는 객체(object)에 접근할 때 string literal 로 접근해야 한다
    • 만약 string type으로 접근하려면 Index Signature를 선언해줘야 한다
/* string type으로 object 접근시 */
const obj = {
  foo: "hello",
}

let propertyName = "foo" // string type
console.log(obj[propertyName]) // error !


/* index signature 추가 */
const obj = {
  [index: string] : string
  foo: "hello",
}

let propertyName = "foo" // string type
console.log(obj[propertyName]) // ok !
  • Object 접근 (javascript vs typescript)
    • Javascript 에서는 object 에 접근할 때 key 타입이 string이 아니면 내부적으로 toString()을 호출한다 (number / obj ..)
    • Typescript 에서는 암묵적 toString()이 호출되지 않음
      --> JS의 암묵적 toString()엉망이라서 호출을 막기 위해 TS에서는 명시적으로 toString()을 하지 않으면 오류를 발생시킴
      --> 그래서 Index Signature를 통해 구조를 명시적으로 두어야 한다
/* JS에서는 암묵적으로 toString() 호출 O */
let obj = { 
  toString(){ 
    console.log('toString called') 
    return 'Hello' 
  } 
} 
let foo:any = {};
foo[obj] = 'World'; // toString called 
console.log(foo[obj]); // toString called, World
console.log(foo['Hello']); // World


/* TS에서는 암묵적 toString() 호출 X */ 
// ERROR: the index signature must be string, number ... 
foo[obj] = 'World'; 
// FIX: TypeScript forces you to be explicit
foo[obj.toString()] = 'World';

그 외

  • boolean
  • number
  • string
  • undefined : Javascript에서 초기화되지 않은 값을 의미
  • null : 초기화 한 후 의도적으로 null값을 할당한 것을 의미
  • bigint : Number가 안정적으로 나타낼 수 있는 최대치인 2^53-1 보다 큰 정수를 표현할 수 있는 내장 객체 BigInt의 타입

사용자 정의 타입 생성

[ 개요 ]

  • 사용자가 직접 타입을 정의해서 사용하는 방법은 크게 2가지가 있다
    • type alias : type 키워드를 통해 새로운 별칭을 가지는 타입을 정의
    • interface : 선언부만 가지는 interface를 통해 객체의 타입을 지정
  • 두 방법은 몇가지 차이점이 있으며, 상황에 따라서 선택해서 사용하면 된다

[ 차이점 ]

  • 확장 방법
    • type alias : & 을 통해 확장
    • interface : extends를 통해 확장
/* type */
type PeopleType = {
  name: string
  age: number
}

type StudentType = PeopleType & {
  school: string
}

/* interface */
interface PeopleInterface {
  name: string
  age: number
}

interface StudentInterface extends PeopleInterface {
  school: string
}
  • 선언적 확장
    • type alias : 동일한 type 선언시 Duplicate 오류가 발생
    • interace : 기존 interface에 필드가 확장
/* type alias */
type Window = {
  title: string
}

type Window = {
  ts: TypeScriptAPI
} // 기존에 동일한 type alias가 존재하므로, duplicate 오류 발생

/* interface */
interface Window {
  title: string
}

interface Window {
  ts: TypeScriptAPI
}
 // Window에는 title과 ts가 모두 존재
  • primitive type 형태
    • type alias : 가능
    • interface : 불가능
/* type alias */
type numberAndObject = number | object

/* interface => 반드시 object 형태를 가짐 */
interface temp{
  numberAndObject
}
  • computed value의 사용
    • type alias : 가능
    • interface : 불가능
// 예제에서 사용되는 in 키워드는 Mapped types임. 즉, 특정 프로퍼티 확인하는 in과 다른 것!
/* type alias */
type names = 'firstName' | 'lastName'
type NameTypes = {
 [key in names]: string
}

/* interface */
const yc: NameTypes = { firstName: 'hi', lastName: 'yc' }
interface NameInterface {
  // error
 [key in names]: string
}
  • 상속 / 확장
    • type alias : type alias끼리 extends, implements 불가능
    • interface : interface는 제한 없음
type PersonAlias = {
    name: string;
    age: number;
}; // type alias

/* {interface} extends {type} 가능 */
interface IPerson extends PersonAlias {
} 
/* {class} implements {type} 가능 */
class PersonImpl implements PersonAlias {
    name: string;
    age: number;

    hello() {
        console.log('안녕하세요');
    }
} // PersonImpl라는 클래스는 PersonAlias라는 인터페이스를 구현하겠다.

[ 정리 ]

  • 합 타입 / 튜플 타입을 반드시 써야되는 상황 -> type alias
  • primitive type -> type alias 사용
  • 그 외 일반적인 상황 -> interface
    • interface는 확장에 대해 열려있기 때문에 Typescript 팀에서 권장

타입 단언 (Assertion)

[ 설명 ]

  • 컴파일러에게 "이 타입의 특정 타입 임을 단언합니다" 라고 말하는 것
  • 다른 언어의 타입 캐스트(Cast) 와 비슷하지만, 특별하 검사나 데이터 재구성을 수행하지 않음
  • 즉, 오직 컴파일 과정에서만 사용되며, 런타임시 영향을 미치지 않는다

[ 방법 ]

1) 앵글 브라켓 (<>) 사용
2) as 키워드 사용

let assertion:any = "타입 어설션은 '타입을 단언'합니다.";
/* 방법 1) 앵글 브라켓 */
let assertion_count:number = (<string>assertion).length;
/* 방법 2) as 키워드 사용 */
let assertion_count:number = (assertion as string).length;

타입 가드 (Type Guards)

[ typeof ]

  • 피연사자의 데이터 타입을 반환하는 연산자
    • string / number / function / object ...
  • typeof nullobject로 반환된다 ?
    • JavaScript의 초기 오류null 타입체크누락되어 발생되는 문제
    • JS측에서는 이미 널리 사용되고 있어서 수정을 하지 않겠다고 한다
      --> 즉, null에 대한 타입 체크별도로 해주어야 한다
      --> 일치 연산자 (===)를 통해서 null을 체크해야 한다
/* typeof */
function checkOutput(myVar: string | number | any){
  if(typeof myVar === 'string'){
    console.log("string type")
  }else if(typeof myVar === 'number'){
        console.log("number type")
  }else{
        console.log("any type")
  }
}

/* object 타입 체크 - null 유의 */
if(yourVariable != null && typeof yourVariable === 'object') {}
if(!!yourVariable && typeof yourVariable === 'object') {}

[ instanceof ]

  • 클래스의 생성자 함수를 통해서 개체가 특정 class 타입인지 확인하는 방법
  • 즉, class의 타입을 확인하기 위한 것
    (일반적인 primitive type에 대해서 사용 불가능)
class Foo {
  foo = 123;
  common = '123';
}

class Bar {
  bar = 123;
  common = '123';
}

function doStuff(arg: Foo | Bar) {
  if (arg instanceof Foo) {
    console.log(arg.foo); // 123
    console.log(arg.bar); // Error!
  }
  if (arg instanceof Bar) {
    console.log(arg.foo); // Error!
    console.log(arg.bar); // 123
  }
}

doStuff(new Foo());
doStuff(new Bar());

[ in ]

  • 특정 속성(property)가 존재하는지 검사하는 연산자
interface A {
  x: number;
}
interface B {
  y: string;
}

function doStuff(q: A | B) {
  if ('x' in q) {
  // q: A
  }
  else {
  // q: B
  }
}

[ 사용자 정의 Type Guard ]

  • type predicates를 사용해서 직접 type guard 역할을 하는 함수를 생성할 수 있다
  • type predicates
    • 형식 : parameterName is Type
    • paramegerName : 현재 함수의 매개변수
    • Type : 확인할 type
/* type predicate를 통해 사용자 정의 type guard 생성 */
function isFish(pet: Fish | Bird): pet is Fish {
  return (pet as Fish).swim !== undefined;
}
let pet = Fish | Bird

if (isFish(pet)) {
  pet.swim();
} else {
  pet.fly();
}

keyof typeof

  • keyof
    • object type을 대상으로 key를 허용하는 type을 반환
    • enum / object 객체 자체에 대상으로 하면 당연히 수행되지 X
  • typeof
    • 타입 가드의 종류로서, 확인된 타입을 string으로 반환
    • 사용할 수 있는 타입이 정해져 있다
      : string / number / object / function ...
  • string literal 타입정의 시 유용하게 사용
    • interface에서는 keyof 로 사용
    • enum / object 에서는 keyof typeof 로 사용

[ interface ]

/* typeof -> 대상이 X */
/* keyof */
interface Brand {
    Nike: 'nike';
    Adidas: 'adidas';
    Puma: 'puma';
}

// a은 'Nike' | 'Adidas' | 'Puma' 타입으로 선언된다
let a: keyof Brand

[ enum ]

enum LogLevel {
  ERROR,
  WARN,
  INFO,
  DEBUG,
}
/* keyof enum */
// enum이 Object니까 Object의 프로퍼티가 들어감 (toString(), replace() ...) 
type b = keyof LogLevel

/* keyof typeof enum */
// LogLevelStrings = 'ERROR' | 'WARN' | 'INFO' | 'DEBUG' 를 가진다
type LogLevelStrings = keyof typeof LogLevel;

[ object ]

  • enum 처럼 keyof typeof 를 통해서 string literal 지정 가능
let LogLevel = {
  error : 'ERROR',
  warn : 'WARN',
  info : 'INFO',
  debug : 'DEBUG',
}
/* keyof typeof enum */
// type LogLevelStrings = 'error' | 'wran' | 'info' | 'debug' 를 가진다
type LogLevelStrings = keyof typeof LogLevel;
profile
Developer & PhotoGrapher

0개의 댓글