index signature에 대한 interface와 type alias의 차이점

nearworld·2023년 5월 17일
0

typescript

목록 보기
26/28

interface로 선언된 타입의 경우 implicit index signature 가 지원되지 않는다고 합니다.
type alias의 경우는 암묵적 index signature 가 적용되어 index signature를 명시적으로 선언하지 않아도 타입 체크를 통과하는 것으로 보입니다.
interface 에서 implicit index signature 가 지원되지 않는 이유는 interfacetype alias와 다르게 선언 병합으로 타입 변경이 가능하기 때문에 추후에 interface가 어떤 속성을 가질지 타입 체커가 예측하는게 불가능하기 때문인 것으로 보입니다.
출처: https://github.com/microsoft/TypeScript/issues/15300

createBottomTabNavigator 함수의 출처는 아래와 같습니다.

import {createBottomTabNavigator} from '@react-navigation/bottom-tabs';

출처인 @react-navigation/bottom-tabs 파일을 보면 아래의 코드가 있습니다.

export { default as createBottomTabNavigator } from './navigators/createBottomTabNavigator';

출처인 ./navigators/createBottomTabNavigator 파일로 이동해보면

export default createNavigatorFactory<
  TabNavigationState<ParamListBase>,
  BottomTabNavigationOptions,
  BottomTabNavigationEventMap,
  typeof BottomTabNavigator
>(BottomTabNavigator);

위 코드가 파일의 마지막 부분에 작성되어 있는 것을 확인할 수 있었습니다.
그래서 실질적으로 createBottomTabNavigatorcreateNaviagatorFactory의 리턴 값임을 확인할 수 있습니다.
드디어 아래의 createBottomTabNaviator의 정체를 확인했습니다.

const BottomTab = createBottomTabNavigator<RootBottomTabParamList>();
export default function createNavigatorFactory<
  State extends NavigationState,
  ScreenOptions extends {},
  EventMap extends EventMapBase,
  NavigatorComponent extends React.ComponentType<any>
>(Navigator: NavigatorComponent) {
  return function <ParamList extends ParamListBase>(): TypedNavigator<
    ParamList,
    State,
    ScreenOptions,
    EventMap,
    typeof Navigator
  > {
    if (arguments[0] !== undefined) {
      throw new Error(
        "Creating a navigator doesn't take an argument. Maybe you are trying to use React Navigation 4 API? See https://reactnavigation.org/docs/hello-react-navigation for the latest API and guides."
      );
    }

    return {
      Navigator,
      Group,
      Screen,
    };
  };
}

위를 참고해보면 아래의 코드가 createBottomTabNaviator 함수임을 알 수 있습니다.

function <ParamList extends ParamListBase>(): TypedNavigator<
    ParamList,
    State,
    ScreenOptions,
    EventMap,
    typeof Navigator
  > {
    if (arguments[0] !== undefined) {
      throw new Error(
        "Creating a navigator doesn't take an argument. Maybe you are trying to use React Navigation 4 API? See https://reactnavigation.org/docs/hello-react-navigation for the latest API and guides."
      );
    }

    return {
      Navigator,
      Group,
      Screen,
    };
  };

createBottomTabNavigator의 제네릭 타입은 아래와 같고 제네릭 타입 제약 조건에는 ParamListBase가 걸려있습니다.

function <ParamList extends ParamListBase> () ...

ParamListBase를 확인해보면 아래와 같이 나옵니다.

export type ParamListBase = Record<string, object | undefined>;

Record 타입임을 알 수 있고 Record 타입은 첫번째 제네릭 타입을 키로 하고 두번째 제네릭 타입을 값의 타입으로 하는 새로운 객체 타입을 만들어내기 때문에 타입의 순회를 위해서 타입에 index signature가 설정되어 있어야하는 것으로 보입니다.
하지만 interface의 경우에는 index signature를 수동으로 세팅해주지 않는 이상 지원되지 않기 때문에
createBottomTabNaviator 함수의 제네릭 타입에 index signature 없는 타입을 대입해줄 경우 에러가 발생한 것으로 보입니다.

index signature를 이용하여 타입을 순회하는 Record 타입이 제약조건으로 걸린 제네릭 타입 ParamListBaseindex signature가 없는 interface 타입을 대입하여 생긴 이슈였음을 확인할 수 있었습니다.

위 이슈에 대해 발견한 해결법은 2가지로 이번 포스트를 마무리합니다.

type alias: 암묵적으로 index signaturer가 지원됨

export type RootBottomTabParamList = Readonly<{
  Home: undefined;
  Calendar: undefined;
}>;

interface: 명시적으로 index signature를 세팅해야 함.

export interface RootBottomTabParamList {
  [k: string]: undefined | object;
  readonly Home: undefined;
  readonly Calendar: undefined;
};
profile
깃허브: https://github.com/nearworld

0개의 댓글