인터페이스로 정의한 객체를 임의의 객체(Record<string, unknown>
)에 할당할 수 없었다. string 타입의 index signature가 인터페이스엔 없다는 오류가 발생했다.
interface FooInterface {
foo: string
}
declare function returnFoo(): FooInterface
declare function acceptObject(obj: Record<string, unknown>): void
acceptObject(returnFoo())
// Argument of type 'FooInterface' is not assignable to parameter of type 'Record<string, unknown>'.
이를 간소화하면,
interface FooInterface {
foo: string
}
const bar: Record<string, unknown> = { foo: '' } as FooInterface
// Type 'FooInterface' is not assignable to type 'Record<string, unknown>'.
// Index signature for type 'string' is missing in type 'FooInterface'.(2322)
인터페이스에 index signature를 정의하거나 type으로 객체를 정의하면 된다.
interface FooInterface {
[key: string]: string | undefined // 명시적 속성의 모든 타입을 가지고 있어야 함
foo: string
}
const bar: Record<string, unknown> = { foo: '' } as FooInterface // OK
하지만 이렇게 하면 FooInterface
로 정의한 객체는 아무 속성이나 접근이 가능해진다.
type FooType = {
foo: string
}
const bar: Record<string, unknown> = { foo: '' } as FooType // OK
엄격한 타입스크립트 세계에서 생각하면 Record<string, unknown>
를 쓰는 일 따위는 하지 말아야 한다. 모든 파라미터와 리턴 값은 명확한 타입으로 정의해서 안전한 타입 시스템을 구축해야한다.
하지만 현실 세계에선 어떤 값이 있는지 모르는 객체를 다룰 일이 많다. 해당 객체를 타이핑할 수 없다면 인터페이스에 인덱스 시그니처를 명시하는 건 어떨까? 리팩토링하고 싶은 욕구가 드는 흔적을 남길 수 있다. 나중에 코드를 보게 될 미래의 내가 리팩토링 하겠지.