사용할 때는 너그럽게, 생성할 때는 엄격하게
보통 매개변수 타입은 반환 타입에 비해 범위가 넓은 경향이 있으며, 실제로도 이 쪽이 사용하기 용이합니다. 이를 테면 다음과 같은 함수의 예를 들 수 있습니다.
interface User {
id: number;
username?: string;
age?: number;
}
const updateUser = (option: User) => {
// ...
};
한편, 반환 타입의 경우에는 선택적인 프로퍼티 없이 더 명확하고 엄격해야 합니다. 실제로 넓은 타입 범위를 갖는 반환 타입은 사용하기가 굉장히 불편합니다. 값을 반환받은 이후에도 타입 체킹을 해주어야하는 일이 다분하기 때문입니다.
const createUser = (option: User): User => {
return {
...option,
}
}
const { username } = createUser({ id: 1, username: '김앨런', age: 27 });
// type username = string | undefined
const firstName = username.charAt(0);
// Object is possibly 'undefined'.
결국 이러한 문제를 해결하려면 기본 형태(반환 타입)와 느슨한 형태(매개변수 타입)으로 각각의 상황에 대한 타입을 별도로 두는 것이 좋습니다.
// 기본 형태
interface User {
id: number;
name: string;
age: number;
}
// 느슨한 형태
// type UserOptions = {
// name?: string | undefined;
// age?: number | undefined;
// }
type UserOptions = Partial<Omit<User, 'id'>>
const updateUser = (id: number, options: UserOptions) => {
// ...
}
let id = 1;
const createUser = (options: UserOptions): User => ({
id: id++,
name: '이름없음',
age: 1,
...options,
})
const user = createUser({ name: '김앨런', age: 27 });