[1] 병합과 컴파일러 속이기

Kim TaeHyeong·2026년 9월 19일

UMC

목록 보기
2/10

0. type과 interface 정의

type Student_type = {
    name: string,
    level: number,
};

interface Student_interface {
    name: string,
    level: number,
}


const Student1: Student_type = {name: "ㅇ", level:0}
const Student2: Student_interface = {name: "ㅇ", level:0}
console.log(Student1, Student2);

{ name: 'ㅇ', level: 0 } { name: 'ㅇ', level: 0 }
로, 똑같다.

컴파일 후, js에서 타입(Student_type, Student_interface)이 사라진다.
ts에서 임의로 만든 type과 interface가 사라짐.

const Student1 = { name: "ㅇ", level: 0 };
const Student2 = { name: "ㅇ", level: 0 };
console.log(Student1, Student2);

js에서는 같을지라도 ts는 타입 정의가 있어 다르다.

1-a. type

type a = {age:number, name:string};
type b = {name:string};
type c = a & b;

멀쩡함

타입이 달라지면 문제가 생긴다!!

type a = {age:number, name:number};
type b = {name:string};
type c = a & b;

name의 type이 never가 되어버림!!

문제는, 합칠 때 오류를 안 보여준다. type c = a & b;

그 후, 할당할 때 오류를 보여준다. (never에 대입 안 되기 때문)

name:number
name:string

type name = number & string;

로, number나 string이 되는 게 아닌 never가 된다.



타입이 never면 할당이 안 된다.
정말일까???

1-b. 속이기

A as B 문법 / A를 타입 B로 간주(개발자를 신뢰)
신뢰했으니 속여보자


2중(이상)이면 컴파일러가 속는다.
끝이 as any as (타입) 꼴이면, 컴파일러가 해당 값을 (타입)으로 신뢰한다.

  1. any가 타입 검사를 끈다. (모든 타입이 다 가능)
  2. 타입 검사를 껐으니 속인다. (as never)


never가 아닌 다른 타입도 가능
텍스트를 숫자로 인식해버린다

이상 값 강제 할당해서 테스트하기는 좋을 듯!!

1-c. 여러가지 비교해보기

type IsEqual<T, U> = 
  (<G>() => G extends T ? 1 : 2) extends 
  (<G>() => G extends U ? 1 : 2) 
    ? true 
    : false;
// ??

뭔지 잘 모르겠지만 두 타입을 엄격하게 비교해준다고 한다
그냥 비교하면 얼렁뚱땅 해버림

type tmp1 = {
    a: string;
}

type tmp2 = {
    a: string;
}
type tmp1andtmp2 = tmp1 & tmp2;


둘이 다른 친구

const test1: tmp1 = {a:"ㅇㅇ"};
const test2: tmp1andtmp2 = {a:"ㅇㅇ"};
console.log(test1 === test2); //false
/* js
const test1 = { a: "ㅇㅇ" };
const test2 = { a: "ㅇㅇ" };
*/

타입은 ts에서 가상으로 설정한 것이므로, js로 컴파일하면 다 사라짐

2-a. interface 중복 처리

interface tmp {a: string};
interface tmp {a: string};

멀쩡함

interface tmp {a: string};
interface tmp {a: number}; //에러

(1) type과 달리, 정의 단에서 에러가 난다.

interface aaa { a: string; }
interface aaa2 extends aaa { a: string; }

interface bbb { a: string; }
interface bbb2 extends aaa { a: number; } //에러


extends 도 동일

interface a1 {a: string}
interface a2 {a: string} // a1과 완전히 같은 내용

type Result2 = IsEqual<a1, a2>;


(2) type과 달리, 같다고 나온다.

interface qwer {
    name: string;
}

const dataBefore: qwer = {
    name: "철수",
};

interface qwer {
    age: number;
}


(3) interface는 위아래가 없다!!!
중간 상태가 없다! 위 아래를 동시에 읽음

2-b. 여러가지

interface a1 {a: string}
interface a2 {a: string} // a1과 완전히 같은 내용

type Result2 = IsEqual<a1, a2>;


같다고 나온다!!

3-a. unknown or union type

function nametest(name: unknown)
{
    if(typeof name === "string")
    {
        return name.toUpperCase();
    }
}

nametest("a")


컴파일러가 똑똑하다.

unknown과 union type을 비교해보자.

function name_string_number(name: string | number)
{
    if (typeof name === "string")
    {
        return name;
    }
    name // number
    if (typeof name === "string")
    {
        name //never
    }
}
  1. 위에서 string이 리턴되니 밑엔 string이 없다. => type이 number만 남음
  2. number인데 string일리가 없으니 never가 된다. => 오류는 안 뜸
function name_unknown(name: unknown)
{
    if (typeof name === "string")
    {
        return name;
    }
    name //unknown
    if (typeof name === "string")
    {
        name //string
    }
}
  1. 위에서 string이 리턴되어도 밑엔 unknown이다.
  2. 그래서 string이 리턴되었음에도, 밑에서 타입이 string이라고 뜬다.
    ( 컴파일 에러 => 런타임 에러 )

실수할 수 있으니, 들어오는 타입들을 안다면, union type이 굿굿이용

function name_unknown2(name: unknown)
{
    if (typeof name === "string")
    {
        return name;
    }
    name //unknown
    if (typeof name === "string")
    {
        name //string
        if (typeof name === "number")
        {
            name //never
        }
    }
}

그래도, string이라고 좁혀졌으면 그 아래선 정상 작동함

3-b. 속이기

function isString(val: unknown): val is string {
    // return이 true면, val은 string이라고 하자는 뜻
    return true;
}
/*
function 함수이름(매개변수: 타입): [매개변수이름] is [바뀌길 원하는 타입] {
    return 참/거짓;
}
*/

ts 상에서만 string이라고 약속한 것
리턴은 boolean

console.log(typeof isString("ㅇ")) //boolean
function unknownTest(a: unknown) {
    if (isString(a)) {
        // 컴파일러는 여기서 a가 string이라고 믿지만, 실제론 ㄴㄴ
        a //string
        a.toUpperCase();
    }
}

unknownTest(12); //런타임 에러

이것도 이상 값을 넣어서 오류 테스트 하기 좋을 듯
( 컴파일 에러 => 런타임 에러 )

4-a. generic

type NamedMember = {
    name: string;
};

function getMemberName<T extends NamedMember>(member: T) {
    return member.name;
}

getMemberName({age:1});//에러
getMemberName({name:"ㅇ", age:1});

extends로 최소 조건 설정

4-b. 속이기

any가 타입 검사를 끄기 때문에 이를 쓰면 된다.

getMemberName({age:1} as any);
getMemberName<any>({age:1});

오류가 안 난다
( 컴파일 에러 => 런타임 에러 )

5. 요약

a. type vs interface

중복처리 : type은 never, interface는 오류
동일구조비교 : type은 ㄴㄴ, interface는 ㅇㅇ
특이점 : interface는 중간이 없음. interface 정의는 코드 위 아래 전체와 소통.
속이기 : (변수) as any as (타입); 으로 타입으로 속이기가 가능.

b. unknown vs union type

타입리턴시 반영 여부 : unknown은 ㄴㄴ, union type은 ㅇㅇ
속이기 : 함수 리턴에 (변수) is (타입) => 변수를 타입으로 가정. 리턴이 true든, false든 상관 ㄴ

c. generic

속이기 : any로 속이기 가능. as any 또는 <any> 로.

속이기의 효능

  1. 심심한가
  2. 코드 리뷰 제대로 하는가
  3. 이상값 넣어도 잘 처리하는가 (무조건 사용자가 해봄)
  4. ts를 과신뢰하진 않는가

0개의 댓글