[Typescript] 기본 문법 총정리

장택진·2023년 8월 1일
0

TypeScript

목록 보기
2/3

타입스크립트는 변수, 매개변수, 리턴값에 타입을 붙이는 것 !

const a: number = 5; // 변수 이름 뒤에 : 타입 (소문자)
const b: string = '5';
const c: boolean = true;
const d: undefined = undefined;
const e: null = null;
const f: any = 123; // 모든타입이 다 됨. 
const g: true = true; // 값을 고정
const h: 5 = 5; //숫자 고정

타입스크립트를 사용하는 목적은 any 타입을 없애는 것

// function 함수
function add(x: number, y: number) // 매개변수 부분의 타입 지정
: number { return x+y }; // return 부분의 타입 지정 :

// type으로 타입을 선언하는 방식 (type alias)
type Add = (x: number, y: number) => number; 
const add: (x: number, y: number) => number = (x, y) => x + y; 

//화살표함수
const add: Add = (x,y) => x + y; // 리턴값이 화살표 뒤에 나옴


// 객체
const obj: 
{ lat: number, lon: number} 
= {lat: 37.5, lon: 127.5}; 

// 배열
const arr: string[] = ['123', '456'] 
const arr2: number[] = [123, 456] 
const arr3: Array<number> = [123, 456] // Generic Type
const arr4: [number, number, string] = [123, 456, 'hello'] // 길이 고정

타입 추론을 적극 활용하자

const b: string = '5'

-> b는 5라는 문자열, 하지만 타입을 string으로 적어줄 경우 b가 5가 아닌 문자열이라는 타입으로 범위가 넓어진다. ( 더 부정확해지는 것 )

  • 타입추론을 정확히 한다면 타입을 명시 안해도 된다.
  • 타입은 최대한 좁게 적기

js 변환 시 사라지는 부분을 파악하자 (npx tsc)

타입을 없애도 올바른 자바스크립트 코드여야 한다.

아래의 타입코드를 자바스크립트로 변환하면 타입코드는 전부 사라짐 (bold처리)
//before (ts)
const f: true = true;  
type Add = () => number;
interface Minus {}
Array<string>

function add(x: number, y: number): number; // 타입 설정
function add(x, y) { // 실제 코드 선언
  return x + y;
}
//after (js)
const f = true;  

function add(x, y) { // 실제 코드 선언
  return x + y;
}

// 문자열 형식을 억지로 다른 타입으로 바꿈 
// as를 사용해서 앞의 타입을 강제로 다른 타입으로 바꿔줌
let aa  = 123;
aa = 'hello' as unknown as number; 

.ts -> function add(x: number, y:number) { return x + y};
.js -> function add(x, y) { return x+y };

never 타입

https://ui.toast.com/weekly-pick/ko_20220323

  • 빈 배열을 선언하면 never 타입 적용
  • const array:never[] -> array 에는 아무 타입도 올 수 없다. 그래서 빈 배열을 선언할 때는 반드시 타입을 지정해줘야 함
try {
  const array: string[] = []; // 타입 지정
  array[0];
} catch(error) {
  error;
}

원시 래퍼 타입, 템플릿 리터럴 타입, rest, 튜플

  • 타입 설정은 소문자로만 !! ex) string
  • control + spacebar : 자동완성 추천
type World = "world";
const a: World = 'world'; 

const b = `hello ${a}`;

// 타입에서도 사용가능
type Greeting = `hello ${World}`; // "hello world" 

type World = "world" | 'hell';
type Greeting = `hello ${World}`; 
// 'hello hell', 'hello world'  타입추천을 정교하게
const c : Greeting = 'hello hell'| 'hello world' 
let arr: string[] = [];
let arr2: Array<string> = [];

function rest(a, ...args: string[]) { // 타입 추가
  console.log(a, args); 
}
rest('1','2','3'); // 1, [2,3]

const tuple: [string, number] = ['1', 1]; 

// 에러처리 o
tuple[2] = 'hello'; 

// 에러처리 x
tuple.push('hello');

enum, keyof, typeof

  • enum
    속성이 순서대로 0,1,2,3, ... 으로 부여된다.
// enum
const enum EDirection {
  Up = 3, 
  Down = 5, 
  Left = 4, 
  Right = 6, 
}
// 만약 값을 지정하지 않았다면 a = 0
const a = EDirection.Up; // a = 3

// object
const ODirection = {
  Up: 0,
  Down: 1,
  Left: 2,
  Right: 3,
} as const;
// as const가 없으면 ->
// Up: number; Down: number; Left: number; Right: number; 
// as const -> 할당한 값을 그대로 사용(정확한 값이 들어감)
enumobject
- 자바스크립트에 코드를 남기고싶지않을때 사용- 자바스크립트에 코드를 남기고싶을떄 사용
- 자바스크립트에서 코드가 사라짐자바스크립트에서 코드가 남아있음
값을 지정해줄수 있음 (=)값을 지정해줄수 있음 (:)

keyof

const obj = { a: '123', b: 'hello', c: 'world'};

// a,b,c만 꺼내오고싶을떄
// typeof : 자바스크립트 값을 type으로 쓰고싶을떄 typeof을 사용
// keyof : 키값만 받아옴
type Key = keyof typeof obj; // type Key = "a" | "b" | "c"

typeof obj 는 obj 객체의 타입을 추론 { a: string, b: string, c: string }
keyof typeof obj는 typeof obj 타입의 모든 속성 키를 유니온 타입으로 추출 
a | b | c

const obj = { a: '123', b: 'hello', c: 'world'} as const;
type Key = typeof obj[keyof typeof obj]; 
// type Key = "123" | "hello" | "world" // value들만 가져옴

typeof obj = { readonly a: '123', readonly b: 'hello', readonly c: 'world' }
keyof typeof obj = 'a' | 'b' | 'c'
typeof obj[keyof typeof obj] = '123' | 'hello' | 'world' 

union( | ) 과 intersection( & )

모든 속성이 다 있어야한다
type A = { hello: 'world' } & { zero: 'cho' }; 
const a: A = { hello: 'world', zero: 'cho'}; 

하나만 있어도 된다.
type A = { hello: 'world' } | { zero: 'cho' }; 
const a: A = { hello: 'world'}; // 가능

타입 에일리어스( Alias )와 인터페이스의 상속 ( extends )

  • Type을 상속의 개념으로 쓸 수 있음
type Animal = { breath: true };
type Poyouryu = Animal & { breed: true };
type Human = Poyouryu & { think: true};

const zerocho: Human = { breath: true, breed: true, think: true };
  • 인터페이스 상속
interface A { breath: true}

interface B extends A { breed: true } // 확장

const b: B = { breath: true, breed: true };
  • 인터페이스 특징

    • 같은이름으로 여러번 선언을 할수있다
    • 선언할떄마다 합쳐진다 -> 다른 사람이 작성한 라이브러리 코드를 수정가능
interface A { talk: () => void; }

interface A { eat: () => void; }

interface A { shit: () => void; }

const a: A = { talk() {}, eat() {}, shit() {}}

Naming Rule

  • 예전에는 타입 지정 변수 앞에 대문자로 I,T,E 처럼 interface,type,enum 을 구분지어줬는데 이젠 IDE에 다 나오기 때문에 거의 안붙임

타입을 집합으로 생각하자 ( 좁은 타입과 넓은 타입 )

  • 좁은 타입에서 넓은 타입으로 대입 가능
  • 반대로 넓은 타입에서 좁은 타입으로 대입은 불가능
type A = string | number; // 넓은 타입
type B = string; // 좁은 타입

# 객체
type A = { name: string };
type B = { age: number}; 

# 넓은 타입
type AB = A | B; 

# 객체는 상세할수록 좁음 ( A & B );
type C = { name: string, age: number };

const ab: AB = { name: 'zerocho'};
const c: C = {name: 'zerocho', age: 29};  

# 잉여속성검사때문에 좁은타입에서 넓은타입에 대입해도 오류가 날수있다. 
# 데이터를 변수로 뺴주고 대입하면 에러가 사라짐
const obj = { name: 'zerocho', age: 29, married: false};
const c: C = obj;

void의 두 가지 사용법

function a() { // function a(): void 로 추론
}

const b = a(); // const b: void 로 추론

# 리턴값이 있으면 에러가  (undefined 가능, null 불가능)
# return을 안적거나, return만 적거나 undefined 

function a(): void { 
  return '3'; // 에러 : 'string' 형식은 'void' 형식에 할당할 수 없습니다.
}
interface Human {
  talk: () => void; // talk 메서드
}

const human: Human = {
  talk() {}    // talk() {return 'abc'} -> 가능
}

void 3가지

  • return 값이 void
    -> (함수에 직접적인 리턴값이 void인 경우에만 return에 값을넣었을떄 오류)
  • 매개변수에 void함수
    -> (리턴값이 존재할수 있음, 리턴값을 무시)
  • 메서드가 void함수
    -> (리턴값이 존재할수 있음, 리턴값을 무시)
function a(): void { // function 선언 void
}

function a(callback: () => void): void { // 매개변수로 선언한 void
}

interface Human { 
  talk: () => void; // 메서드로 선언할떄 void 두개의 역할이 조금 다름
}

declare
forEach 선언을 다른 파일에서했을때, declare를 사용하면,
forEach 외부에서 선언했음을 보증

declare function forEach<T>(arr: T[], callback: (el: T) => undefined): void; 
// declare function forEach<T>(arr: T[], callback: (el: T) => void): void;
let target: number[] = [];
forEach([1, 2, 3], el => target.push(el));

unknown과 any( 타입 대응 표 )

interface A {
  talk: () => void;
}
const a: A = {
  talk() { return 3; }
}


# unknown vs any

# any 를 사용하면 이후로 타입스크립트가 타입검사를 포기함 
const b: any = a.talk(); 
b.method();

# unknown 을 사용하면 사용자가 직접 b의 타입을 정해줌. 정해진 타입만 사용가능
const b: unknown = a.talk(); 
(b as A).talk();

# unknown)
try {
} catch (error) { // var error: unknown
  error.message // -> (error as Error).message 
}

타입 좁히기( 타입 가드 )

function numOrStr(a: number | string) {
  if (typeof a === 'string') {
    a.split(',');  
  } else {
    a.toFixed(1);
  }
}


function numOrNumArr(a: number | number[]) {
  if (Array.isArray(a)) { // number[]
    a.slice(1);  
  } else {                // number
    a.toFixed(1);
  }
}
numOrNumArr(123);
numOrNumArr([1, 2, 3]);

# 클래스 자체의 타입은 typeof 클래스(typeof A)입니다.
class A {
  aaa() {}
}
class B {
  bbb() {}
} 
function aOrB(param: A | B) {
  if (param instanceof A) { // Class 간에는 서로 instanceof 로 구별한다
    param.aaa();
  }

}
aOrB(new A()); 
aOrB(new B());

type B = { type: 'b', bbb: string };
type C = { type: 'c', ccc: string };
type D = { type: 'd', ddd: string };
type A = B | C | D;

# 값으로 구분
function typeCheck(a: A) {
  if (a.type === 'b') {
    a.bbb;
  } else if (a.type === 'c') {
    a.ccc; // 타입추론 : (parameter) a: C
  } else {
    a.ddd;
  }
}

# 속성명으로 구분
function typeCheck(a: A) {
  if ('bbb' in a) { // in 연산자 // a객체안에 bbb라는 속성이 있으면
    a.type; // a: B
  } else if ('ccc' in a) {
    a.ccc; 
  } else {
    a.ddd;
  }
}

# 객체에 태그(라벨)을 달아놓는 습관 중요, 타입검사할떄 편함

# 객체들간 구별할때 2가지 방식을 사용
(1)
const human = { type: 'human' };

(2)
const human = { talk()};
if ('talk' in a) {
}

커스텀 타입 가드 ( is, 형식 조건자 )

  • type을 구분해주는 커스텀 함수를 직점 만들 수 있음
# 리턴값에 is가 들어가있으면 커스텀 타입가드 함수이다
function catOrDog(a: Cat | Dog): a is Dog { 
 # 타입 판별을 직접 만들기 ( a 에 meow라는 속성이있으면 false, 그렇지 않으면 true )
  if ((a as Cat).meow) { return false } 
  return true;
}

const cat: Cat | Dog = { meow: 3 }
if (catOrDog(cat)) {
    console.log(cat.meow);
}
if ('meow' in cat) {
    console.log(cat.meow);
}
  • 타입스크립트가 타입 추론을 잘못 할 때 custom type guard를 만들어서 정확한 type을 추론할 수 있음
const isRejected = (input: PromiseSettledResult<unknown>): 
input is PromiseRejectedResult => input.status === 'rejected';

const isFulfilled = <T>(input: PromiseSettledResult<T>): 
input is PromiseFulfilledResult<T> => input.status === 'fulfilled';

const promises = await Promise.allSettled
([Promise.resolve('a'), Promise.resolve('b')]);
const errors = promises.filter(isRejected);

{} 와 Object

  • v. 4.8 이상 새로운 타입 -> {}, object
# {}, Object: 모든 타입(null, undefined 제외)
빈객체 {}
const x: {} = 'hello'; 

# 대문자 Object  
const y: Object = 'hi'; 

# 객체 ( 객체 지양, 대신 interface, type, class 사용하기)
const xx: object = 'hi'; 
const yy: object = { hello: 'world' }; 

const z: unknown = 'h1';

# 버전 4.8 unknown : 모든값을 다 받을수있음  unknown = {} | null | undefined 

// 과거에는 unknown인 변수를 if문안에 넣으면 그대로 unknown이 나옴
// 4.8 버전부터는 unknown인 변수를 if문안에 넣으면 unknown = {} | null | undefined 

if (z) {
  z; // {}
} else {
  z; // null | undefined 여기에 속함
}

readonly, Index Signature, Mapped type

  • readonly
interface A {
  readonly a: string;
  b: string;
}

const aaaa: A = {a: 'hello', b: 'world'};

# 에러 -> 읽기 전용 속성이므로 'a'에 할당할 수 없습니다.
aaaa.a = '123'; 
  • Index Signature
# 어떤 키든 전부다 같은 속성으로 지정가능 
type A = { [key: string]: string }; 
const aaaa: A = { a: 'hello', b: 'world' };
  • Mapped type
type B = 'Human' | 'Mammal' | 'Animal';

# key 가 B3하나 (키 줄이기)
type A = { [key in B]: number }; 
const aaaa: A = { Human: 123, Mammal: 5, Animal: 7 };

type A = { [key in B]: B }; 
const aaaa: A = { Human: 'Animal', Mammal: 'Human', Animal: 'Mammal' };

class의 새로운 기능들

class A {
  a: string;
  b: number;
  constructor(a: string, b: number = 123) {
    this.a = a;
    this.b = b;
  }
  method() {

  }
}
# 2번쨰 인수의 기본값이 이미 지정되어있기떄문에 필수아님 (2번쨰 인수 지정하면 덮어씀)
const a = new A('123'); 

constructor : 생성자의 매개변수를 받을 수 있음

  • class의 이름은 그 자체로 타입이 될 수 있음. 하지만, new A() -> instance를 나타냄
  • class 타입 typeof
type AA = A;
const a: A = new A('123');
const b: typeof A = A;
  • class 추가된 문법
# implements, private, protected 타입스크립트에 있는 키워드
# class 구현, interface 추상 
# classinterface를 따라야함 -> a, b속성

interface A {
  readonly a: string;
  b: string;
}

class B implements A { 
  # class의 모양을 interface에서 통제가능
  a: string = '123'; 
  b: string = 'world';
}
class C extends B {}
new C().a;
new C().b;

# 자바스크립트로 바꿧을떄
class B{
  constructor() {
    this.a = '123';
    this.b = 'world';
  }
}
class C extends B {

}
new C().a;
new C().b;

interface A {
  readonly a: string;
  b: string;
}
# private, protected : 객체지향에서 사용
class B implements A { 
   private a: string = '123'; 
  protected b: string = 'world';
  c: string = 'wow'; 

  method() {
    console.log(this.a);  // o
    console.log(this.b);  // o
    console.log(this.c);  // o
  }
}
class C extends B {
  console.log(this.a); // x
  console.log(this.b); // o
  console.log(this.c); // o


}
# instance
new C().a; // x
new C().b; // x
new C().c; // o

옵셔널, 제네릭 기본

  • optional : 속성뒤에 위치 -> ?
// abc(...args: number[]) -> 전부다 받고싶을떄 이거씀
 function abc(a: number, b?: number, c: number?) {}
 abc(1) // o
 abc(1, 2) // o
 abc(1, 2, 3) // o
 
 let obj: { a: string, b?: string }  = { a: 'hello', b: 'world' } // b 필수 x
 obj = { a: 'hello' };
  • Generic : 타입이 뭔지 모를 때 사용, 사용할 때 타입이 정해짐
# 같은 타입은 같은 알파벳으로 지정
function add<T>(x: T, y: T): T { return x + y }

add<number>(1, 2);
add(1, 2);
add<string>('1', '2');
add('1', '2');
add(1, '2');

# 타입에 제한을 두고싶을떄 -> Textends를 붙이면 T가 제한됨
function add<T extends string>(x: T, y: T): T { return x + y } 
function add<T extends string | number>(x: T, y: T): T { return x + y } 
function add<T extends number, K extends string>(x: T, y: K): T { return x + y } 


function add<T extends {a: string}>(x: T): T {return x};
add({a: 'hello'})

function add<T extends string[]>(x: T): T {return x};
add(['1', '2', '3'])

콜백함수 형태를 제한
function add<T extends (a: string) => number>(x: T): T { return x};
add((a) => +a)

// <T extends {...}> // 특정 객체
// <T extends any[]> // 모든 배열
// <T extends (...args: any) => any> // 모든 함수
// <T extends abstract new (...args: any) => any> // 생성자 타입
// <T extends keyof any> // string | number | symbol

기본값 타이핑

const a = (b = 3, c = 5) => { // const a = (기본값) => {
  return '3';
}

const a = (b: number = 3, c: number =5) => { 
  return '3';
}

# 매개변수의 기본값 객체
const a = (b: { children: string } = {children: 'zerocho'}) => { 

}

# 리액트 .jsx에서 <T> 사용시 에러가 날수있으니, 기본값 | extend  설정해주기
const add = <T = unknown>(x: T, y: T): T => { { x, y} }; 
const result - add(1,2);

참고자료 : 제로초 ts-all-in-one

profile
필요한 것은 노력과 선택과 치킨

0개의 댓글