@tanstack/react-query에 대한 TypeScript 설정 고군분투 (a.k.a ts2742)

Jung Wish·2024년 2월 26일
2

오늘의_코딩

목록 보기
2/11

@tanstack/react-router + @tanstack/react-query 조합을 써보려고 @tanstack/react-query를 5버전으로 올린 후(queryOptions와 useSuspenseQuery hook을 써보기 위함) queryOptions를 사용해 custom function을 만들었더니 갑자기 type 참조를 못하겠다며 우수수수수수수 오류가 자기 주장을 해왔다. returnType에 대한 타입참조를 찾지 못해 반환값 추정이 안된단다...😬

일단 문제가 되는 부분은 다행히(?) 모두 types 참조를 못하고 있다는 에러만을 보여주고 있었다.
명확히 어디서 문제가 발생했는지는 솔직히 잘 모르겠지만? pnpm workspace 참조의 문제, typescript bundler resolution 문제, monorepo의 구조적 문제 정도를 의심해봤다. 일단 react-query v5 + vite(react-ts-swc)에서만 문제가 발생해서 가장 유력한건 typescript 설정이었다. 일단 그래서 구글링을 열심히 해봤다.

여기서도 보면 비슷하게 이야기를 나누고 있다. 어쨌든 tanstack 측에서는 tsup이 문제인거 같다고 한다.

https://github.com/TanStack/query/issues/6318

그러니까 원래는 UseQueryReturnType에 대한 dts 파일을 생성한 후, 이를 index.d.ts 진입점에서 다시 같은 이름으로 export를 해줘야하는데, tsup이 트랜스파일 및 번들링을 진행하면서 특정 이름으로 alias를 시켜 간접적으로 export를 한 후 index.d.ts 진입점에서 재반환을 시키고 있다는 것이다. 이 간접 참조 및 alias가 TypeScript의 인식에 혼란을 주고 있다고 한다.

그래서 node_modules로 들어가서 모듈 코드를 까봤는데..? 내 패키지엔 alias가 없는데..? 애초에 다른 패키지 얘기기도 했지만 오류 양상이 비슷해서 봤었는데 내 case의 경우 저 내용이 직접적인 원인은 아닌것 같다. 일단 저 discussion에서는 TypeScript 5.4.0-beta 버전으로 마이그레이션하면 이슈가 없어질거라는데 굳이 beta 버전을 깔고싶지 않았고 alias 케이스가 아니어서 다른 코멘트의 내용을 기반으로 tsconfig 수정 고군분투를 해봤다.

일단 여러가지 시도중에서 유의미했던 방법은 4가지가 있다.
1. tsconfig.json의 moduleResultion을 'node'로 설정하기

  • 이렇게하면 에러가 없는것처럼 보여지지만? build를 시작하면 에러가 발생한다. 일단 vite 초기 설정이 bundler이고 여기에 맞춘 플러그인과 세팅을 해두었기 때문인것 같다. 그래서 이 방법도 아닌것 같다. 이거 하나 고치려고 너무 대규모 수정을 거쳐야 하므로 pass..
  1. @react-query/query-core 라이브러리 설치하기
  • 아무래도 react-query에 dependency가 있는 query-core에서 참조에러가 나오는것 같아서 일단 혹시?하는 마음으로 깔아봤다. 놀랍게도 동작한다...(왜..? 때문에..?)
  • 근데 이 방법도 역시 아닌것 같다. react-query dependencies로 .pnpm에 이미 깔려있는데 굳이 또 까는것은 불필요하다.
  1. declare module로 인식 못하는 모듈을 선언해준다. 또는 직접적으로 사용하려는 type을 import 해준다.
  • 이게 근데 또 해당 에러가 발생하는 파일 즉, declaration file 인식을 못하는 파일에 죄다 추가를 해줘야한다. 매우 불편하다.
  • 자체 d.ts 파일을 만들어서 declare module 해봐도 전역으로 적용이 안된다..(왜..???)
import { queryOptions } from '@tanstack/react-query';
import type * as _T from '@tanstack/react-query'; // 이 부분 추가

export type User = {
  access_token: string;
  email: string;
  marketing: boolean;
};

const fetcher = (...args: Parameters<typeof fetch>) => fetch(...args).then(res => res.json());

export const workspaceQueryOptions = (name: string) =>
  queryOptions({
    queryKey: ['workspace', name],
    queryFn: () => fetcher(`/api/settings/${name}`),
    staleTime: 30 * 60 * 1000, // 30 min
  });

export const workspaceAPITokenQueryOptions = (name: string) =>
  queryOptions({
    queryKey: ['workspace', name, 'apiToken'],
    queryFn: () => fetcher(`/api/workspace/${name}/api_token`),
    staleTime: Infinity,
  });
export const userQueryOptions = () =>
  queryOptions<User>({
    queryKey: ['auth'],
    queryFn: () => fetcher('/api/account'),
    staleTime: 10 * 60 * 1000, // 10 min
  });
  1. tsconfig.json paths에 인식못하는 module path에 대해 올바른 경로를 등록하기.
  • 현재 문제가 되는 TypeScript path 인식은 react-query package에서 import하는 query-core와 .types의 경로였다. 그래서 문제 메세지가 원하는대로 path mapping을 지정해봤다. 그러니까 전역적으로 동작했다.
  • 근데 이 방법도 매번 다른 dependencies type을 못찾을때마다 등록해야하는 점 + .types 처럼 일반적인 이름에 paths mapping을 걸면 해당 명칭을 쓸 수 없는 점 때문에 적절하지 않다고 판단했다.
{
  "extends": "tsconfig/vite.json",
  "compilerOptions": {
    "target": "es2022",
    "useDefineForClassFields": true,
    "lib": ["esnext", "DOM", "DOM.Iterable"],
    "module": "es2022",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "Bundler",
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "jsxImportSource": "@emotion/react",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    "baseUrl": ".",
    "paths": {
      "./types": ["node_modules/@tanstack/react-query/build/modern/types.d.ts"], // 여기랑
      "@tanstack/core/*": ["../../node_modules/.pnpm/@tanstack+query-core@5.22.2/node_modules/@tanstack/query-core/*"], // 여기를 추가
      "@/*": ["./src/*"]
    },
    "skipDefaultLibCheck": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"],
  "references": [{ "path": "./tsconfig.node.json" }]
}
  1. tsconfig.json declaration(+declarationMap)을 false로 주기.
  • declaration이 true인 경우에 해당 에러가 나온다는 코멘트도 있어서(in vite) false 옵션을 지정해주었다.
  • 처음에 declaration만 false로 주니 또 요상한 에러가 나오길래 declarationMap까지 일괄로 false로 주니까 에러가 사라졌다. TypeScript 최신에서만 그러는 것 같다. 5.2, 5.0에서는 이런일이 없었다. (참고로 해당 프로젝트 typescript version 5.3.3)
  • declaration은 declaration file(d.ts)을 만들 것인지의 여부이고 declarationMap은 원본 .ts파일로 연결되는 sourceMap을 만들것인지의 여부를 설정하는 것이다.
  • 정확히 왜 고쳐졌는지 100% 이해는 안되지만...? 에러가 난 부분이 특정 라이브러리 모듈을 import해서 custom 함수 및 모듈을 만들었을때 ReturnType을 추론해낼 수 없다 것이었는데 이는 관련된 라이브러리 type path 인식을 못해 적절한 declaration file을 만들 수 없어서 발생하는 에러인 것 같다. 해당 bundler module 형식의 ts(x) 파일들에 d.ts expose를 제한하면서 react-query와 관련된 type declaration도 내보내질 필요가 없으니 오류가 사라진 것으로 추정하고 있다.
  • 나는 일단 해당 앱을 특별히 js 라이브러리로 만들 예정이 없고 단일 앱으로 쓸 것이라 특별히 declaration file이 필요없기 때문에 일단 해당 방식을 사용하기로 했다.

{
  "extends": "tsconfig/vite.json",
  "compilerOptions": {
    "target": "es2022",
    "useDefineForClassFields": true,
    "lib": ["esnext", "DOM", "DOM.Iterable"],
    "module": "es2022",
    "skipLibCheck": true,

    /* Bundler mode */
    "moduleResolution": "Bundler",
    "declaration": false,
    "declarationMap": false,
    "allowImportingTsExtensions": true,
    "resolveJsonModule": true,
    "isolatedModules": true,
    "noEmit": true,
    "jsx": "react-jsx",
    "jsxImportSource": "@emotion/react",

    /* Linting */
    "strict": true,
    "noUnusedLocals": true,
    "noUnusedParameters": true,
    "noFallthroughCasesInSwitch": true,

    "baseUrl": ".",
    "paths": {
      "@/*": ["./src/*"]
    },
    "skipDefaultLibCheck": true
  },
  "include": ["src"],
  "exclude": ["node_modules", "dist"],
  "references": [{ "path": "./tsconfig.node.json" }]
}

오늘의 결론은..버전은 함부로 올리지말자..?

profile
Frontend Developer, 올라운더가 되고싶은 잡부 개발자, ISTP, 겉촉속바 인간, 블로그 주제 찾아다니는 사람

0개의 댓글