일반적으로 타입스크립트의 타입을 사용하는 가장 큰 이유 중 하나가 타입 별칭이라는 것에 모두가 동의 할 것 이다
두개의 함수에서 key는 중복되는 타입이며 이를 별칭을 사용하여 dry 하지 않게 관리하는게 목표
| 하나를 수정하면 나머지 하나도 바꿔야함 | 하나만 바꿔도 나머지가 바뀜 |
|
|
그러나 우리는 강제로 dry 한 타입을 많이 생성 할 수 밖에 없다
| getUser.ts(api fetch) | UserInfo.tsx (ui) |
|
|
isCheck 라는 키는 ui 에서만 사용되는 기능이기에 새로운 타입이 하나 더 생긴다.
dry 하지않게 기존 getUser.ts 에서 사용하는 타입을 상속시켜 만들자
import { UserSchema } from './getUser.ts'
type User = (UserSchema & {
isCheck: boolean;
})
여기서 추가로 기획자가 체크된 유저위에 마우스 hover 시 유저 계좌번호를 보여달라고한다, !유저 이름을 제외한
컴포넌트를 하나 더 만들어 아래 타입을 생성한다.
improt { User } from './UserInfo'
// 중복된 이름 발생 🚩
type User = Omit<User, "name"> & { account: string}
const user: User= {
account: "123-444-55",
isCheck: true,
}
추가로 기획상 개인정보 화면에는 유저의 계좌번호가 나오는 화면이 존재한다
import { User } from './UserInfo.tsx ' // name 을가진 api 유저
import { User } from './UserHoverAccount.tsx ' // account 를 가진 유저
// 중복된 이름 🚩
type User = Pick<User, "name"> & User
추가로 작업하다 보니 UserInfo컴포넌트를 wrapping 할 경우가 생겼다
import UserInfo, { User } from './UserInfo';
type Props = {
userInfoProps: User;
isLogin: boolean;
}
// 중복 🚩
export type User = User & Pick<Props, "isLogin">;
function UserProfile(props: Props) {
return <UserInfo {...props} />
}
// isCheck 키를 가진 유저가 필요하다 누구꺼 ? 아무거나 사용하자.. 🚩
import { User } from './Userinfo';
import { User } from './UserProfile';
// 그리고 또 User 생성 ? 🚩
위 요구사항은 굉장히 일반적으로 상당히 생각보다 잦다... 중복 이름을 방지하기 위해서 여러분들은 얼마나 다양한 이름의 유저를 생성하였는가 ?
추가로 우리는 타입을 export 하였다.
import 구문의 from 절은 컴포넌트를 어디서 import 하냐에 따라 별칭이 사용되기도 하며 상대경로를 사용하기도 한다
import { User } from '../../UserInfo';
import { User } from '../../../UserInfo';
import { User } from '@/UserInfo';
이말은 특정 요구사항으로 인해 특정 유저타입의 이름이나 키 속성이 변경된다면 검색으로 해당 유저타입을 사용하는 곳을 찾는 것은 불가능에 가까워지게 된다는 의미다
물론 빌드 혹은 lint 명령어로 부서진곳을 찾을 수 있다
그럼 네임스페이스를 이용하면 이 문제가 해결되는 것인가 ?
절대 아니다
네임 스페이스를 사용 해도 여전히 문제는 존재한다.
다만 한가지 확실한건
namespace UserInfo {
// User는 User다
export interface User extends UserSchema {
isCheck: boolean;
}
}
namespace UserHoverAccount {
// User는 User다
export interface User extends Omit<UserInfo.User, "name"> {
account: string;
}
}
namespace MyProfile {
// User는 User다
export interface User extends UserInfo.User, UserHoverAccount.User {}
}
namespace UserProfile {
export interface Props {
userInfoProps: UserInfo.User;
isLogin: boolean;
}
// User는 User다
export interface User extends UserInfo.User, Pick<UserProfile.Props, "isLogin"> {}
}
타입 이름대한 속박이 해제된다 User는 User고 Box는 Box며 SelectedItem은 SelectedItem이다
뿐만 아니라 특정 컴포넌트에 속한 User를 수정할 경우 사용하는 곳을 아주 명확하게 파악 할 수 있다.
네임스페이스를 가진 이름이 존재하기때문에 빌드와 lint를 돌려야만 부서진 곳을 찾을 수 있는 불확실성을 없앨 수 있고 자신있게 컴포넌트를 리팩토링 할 수 있게 된다
// 이 User를 Import 하는 곳은 누구며 무엇이 필요한 것일까, 알 수 없으며 검색해도 찾기가 사실상 불가능
export type User = { name: string };
namespace UserInfo {
// 누군가 이 User 를 import 한다면 UserInfo 컴포넌트의 User를 사용하기 위해서 가져 가는 구나 라는 확실성
export interface User { name: string }
}
추가로 다른 User 타입을 improt 하기 위한 절차가 생략된다
이부분은 사용처마다 다르겠지만 기본적으로 아래와 같이 타입 import 생략이 가능하다
// import는 UserInfo 컴포넌트만!
import UserInfo './UserInfo';
function Some() {
// UserInfo 컴포넌트의 Props에 접근 가능
const name: UserInfo.Props = { name: "jim" }
return <UserInfo />
}