특징

1. 인터페이스 병합

<aside>

// 기존에 정의된 인터페이스
interface IPerson {
    name: string;
    age: number;
}

// 현재 스코프에서 동일 이름으로 추가 정의하여 확장
interface IPerson {
    address: string;
}

</aside>

2. extends

<aside>

interface IPerson {
    name: string;
    age: number;
}

interface IPerson2 extends IPerson {
    [key: string]: unknown; // 인덱스 시그니처 추가
}

</aside>

3. 인덱스 시그너처에 대한 엄격한 호환성 검사

<aside>

image.png

interface IPerson {
    name: string;
    age: number;
}

interface IPerson2 extends IPerson {
    [key: string]: unknown; // 인덱스 시그니처 추가
}

type Person = {
    name: string;
    age: number;
}

const callUnknown = (person: Record<string, unknown>) => {};
callUnknown({ name: "Alice", age: 30 } as IPerson); **// =======> Error**
callUnknown({ name: "Alice", age: 30 } as IPerson2);
callUnknown({ name: "Alice", age: 30 } as Person);

const callAny = (person: Record<string , any>) => {};
callAny({ name: "Bob", age: 25 } as IPerson);
callAny({ name: "Bob", age: 25 } as IPerson2);
callAny({ name: "Bob", age: 25 } as Person);

설명

Record<string, unknown> 타입은 실제로 { [key: string]: unknown] } 꼴의 인덱스 시그니처가 하나 정의된 오브젝트 형태의 타입이다.

type은 구조적 호환성을 더 유연하게 판단하며, interface처럼 ‘확장 가능성’을 고려하지 않는다.

따라서 필요한 프로퍼티만 존재한다면 할당 가능으로 판단한다.

반면에 interface는 확장(동일 이름으로 중복 선언) 가능성을 고려하여, 더 엄격하게 타입을 판단한다.

따라서 위 예제의 경우에는 [key: string]: unknown] 인덱스 시그니처가 인터페이스 내에 명시적으로 존재하지 않기 때문에, 호환이 불가능한 것으로 판단하여 에러가 발생한다.

</aside>