Today I Learned_230220

3 분 소요

TypeScript

인프런 타입스크립트 강의 수강

// A가 더 넓은 타입에 해당
type A = string | number; //string 또는 number
type B = string;
type C = string & number; // 이거는 불가능(never 타입으로 나온다)
  • A가 B보다 더 넓은 범위 (A > B > C 순)
  • any는 전체집합, never는 공집합으로 볼 수 있다.
  • 좁은 타입에서 넓은 타입으로 대입 가능 (A에 B 넣을 수 있다)
  • 넓은 타입에서 좁은 타입으로 대입 불가능 (B에 A 넣을 수 없다)
type D = {name : string};
type E = {age: number};
type F = {name: string, age:number};
//F는 오히려 좁은 타입이다. 속성이 적을수록 넓은 타입이다.
//객체는 상세(구체적일수록)할수록 더 좁은 타입이다.

type DE = D | E;
type DEF = D & E;

const de: DE = {name: 'heejung'};
const def: DEF ={name: 'heejung', age:29};
//const def: DEF ={name: 'heejung', age:29, married:false}; -> 이건 안 된다.
  • 넓은 범위인 DEF에 구체적인 것을 넣었는데 왜 안되는거지? -> 객체 리터 잉여 속성 검사
  • 객체 리터럴을 바로 넣으면 잉여 속성 검사(추가적인 검사) 진행
  • 타입이 넓은지 좁은지에 이어 잉여 속성 검사를 추가적으로 진행한다.
  • 함수의 경우 공변성/반공변성이 있다.
  • 잉여 속성 검사 - 중간에 다른게 껴 있으면 검사하지 않는다. 바로 대입 시 참고할 것
type A = { hello: string };
const a: A = { hello: 'world', why: 'error' }; //이건 안 됨

const b = { hello: 'world', why: 'error' };
const c: A = b;  //이건 됨

interface vs. type alias

//interface는 된다.
interface A {a:string}
interface A {b:string}
const obj1: A = {a: 'hello', b:'world'}

//type은 안 된다.
type B = {a:string}
type B = {b:string}
const obj2: B = {a:'hello', b:'world'}
//아래와 같은 상황에서 b는 자동으로 a를 void로 추론
function a() {

}

const b = a();
  • void 타입은 return값을 사용하지 않겠다는 뜻(메서드나 매개변수에서는 리턴값 사용 가능, but 조심해야 함)
  • function 선언할 때의 void와 메소드에서의 void는 다르다!
  • undefined로 리턴하는 것은 가능
  • null로 리턴하는 것은 불가능
  • void는 리턴값을 아예 안 주거나 / return만 주거나 할 때 사용
  • void는 총 3군데에서 사용
    • 직접적인 리턴 값이 void인 경우 → 리턴값 있으면 안 됨
      • 메소드의 리턴값을 사용하지 않겠다는 의미.
    • 매개변수에 void함수가 들어간 경우 → 리턴값 있어도 됨
      • 리턴값이 뭐든간에 상관하지 않겠다
    • 메소드에 void함수가 들어간 경우 → 리턴값 있어도 됨
      • 리턴값이 뭐든간에 상관하지 않겠다
function a(): void {
    return 3; // void로 되어 있으면 여기서 에러가 난다.
}

//declare를 선언하면 구현부를 만들지 않아도 됨
//declare는 자바스크립트로 변환할 때 사라진다.
//undefiend -> 이 상태면 에러가 난다.
declare function forEach<T>(arr: T[], callback: (el: T) => undefined): void;
// 이렇게 void로 선언하면 오류가 나지 않는다.
// void의 리턴값이 무엇인지 상관하지 않겠다는 의미
// declare function forEach<T>(arr: T[], callback: (el: T) => void): void;
let target: number[] = [];
forEach([1, 2, 3], el => target.push(el)); //callback의 리턴값은 number인데 undefined로 정의하고 있기 때문에

//declare 없으면 아래와 같이 구현해야 함
function forEach(arr: number[], callback: (el: number) => undefined): void;
function forEach() {
}

interface A {
    talk: () => void;
}
const a: A = {
    //talk() => { return undefined}
    talk() { return 3; } //그런데 이게 가능
}
  • 타입만 선언하고 싶을 때 declare(구현은 다른 파일에 있어야 함)
  • 다른 파일에 구현되어 있다는 것을 보장할 때 사용
    • js에서는 다른 script 태그에 들어있다고 생각하기 때문에 js로 변환할 때는 문제가 되지 않는 것 처럼 보인다.
declare const a: string;
declare function a(x: number): number;
declare class A {}

// 추후 declare module, declare global, declare namespace도 배움
  • void인지 아닌지에 따라 함수 표현에 제한이 된다.
declare function forEach(arr: number[], callback: (el: number) => undefined): void;

// 이 경우 둘 다 안 됨
forEach([1, 2, 3], el => {target.push(el)});
forEach([1, 2, 3], el => target.push(el));
declare function forEach(arr: number[], callback: (el: number) => number): void;

forEach([1, 2, 3], el => {target.push(el)}); //얘는 void 리턴이라 안 됨
forEach([1, 2, 3], el => target.push(el));
declare function forEach(arr: number[], callback: (el: number) => void): void;

//둘 다 돌아간다.
forEach([1, 2, 3], el => {target.push(el)});
forEach([1, 2, 3], el => target.push(el));
  • void는 undefined와 다르다.
  • 함수 리턴값의 void와 매개변수/콜백의 void, 메서드의 void는 서로 다르다.

카테고리:

업데이트: