타입 힌팅 개요

GitLab 프로젝트의 프론트엔드 코드베이스는 현재 타입을 요구하거나 강제하지 않습니다. 타입 주석 추가는 선택 사항이며, 우리는 현재 JavaScript 코드베이스에서 어떤 타입 안전성도 강제하지 않습니다. 그러나 타입 주석은 코드베이스에 명확성을 추가하는 데 매우 유용할 수 있습니다. 특히 공유 유틸리티 코드에서 그렇습니다. 이 문서는 현재 타입 힌팅이 어떻게 작동하는지, 새로운 타입 주석을 추가하는 방법, 및 GitLab 프로젝트에서 타입 힌팅을 설정하는 방법을 다루고자 합니다.

JSDoc

JSDoc는 JavaScript 코드에서 타입을 문서화하고 설명하는 도구로, 특별히 형성된 주석을 사용합니다. JSDoc의 타입 용어는 상대적으로 제한적이지만, 많은 IDE들 에 의해 널리 지원됩니다.

예제

함수 설명하기

함수 타입을 설명하기 위해 @param@returns를 사용합니다:

/**
 * 두 숫자를 더합니다.
 * @param {number} a 첫 번째 숫자
 * @param {number} b 두 번째 숫자
 * @returns {number} 두 숫자의 합
 */
function add(a, b) {
    return a + b;
}
선택적 매개변수

매개변수 이름 주위에 대괄호 []를 사용하여 선택적임을 표시합니다. 기본값은 [name=value] 구문을 사용하여 제공할 수 있습니다:

/**
 * 두 숫자를 더합니다.
 * @param {number} value
 * @param {number} [increment=1] 선택적 매개변수
 * @returns {number} 두 숫자의 합
 */
function increment(a, b=1) {
    return a + b;
}
객체 매개변수

객체를 허용하는 함수는 @param 이름에서 object.field 표기를 사용하여 타입을 지정할 수 있습니다:

/**
 * 두 숫자를 더합니다.
 * @param {object} config
 * @param {string} config.path 경로
 * @param {string} [config.anchor] 앵커
 * @returns {string}
 */
function createUrl(config) {
    if (config.anchor) {
        return path + '#' + anchor;
    }
    return path;
}

즉시 값을 할당하지 않은 변수의 타입 주석 달기

도구와 IDE는 즉시 값을 받지 않는 값의 타입을 추론하기 어렵습니다. @type(https://jsdoc.app/tags-type) 표기를 사용하여 이러한 변수에 타입을 할당할 수 있습니다:

/** @type {number} */
let value;

더 많은 문법 세부사항은 JSDoc 공식 웹사이트를 참조하세요.

JSDoc 사용 팁

기본 타입에 소문자 이름 사용하기

대문자 Boolean과 소문자 boolean 모두 허용되지만, 대부분의 경우 원시값이나 객체가 필요할 때는 소문자 버전이 올바른 선택입니다: boolean, number, string, symbol, object.

/**
 * `text`를 변환합니다.
 * @param {string} text - 변환할 텍스트
 * @returns {string} 변환된 텍스트
 */
const gettext = (text) => locale.gettext(ensureSingleLine(text));

잘 알려진 타입 사용하기

HTMLDivElement 또는 Intl과 같은 잘 알려진 타입은 사용 가능하며 직접 사용할 수 있습니다:

/** @type {HTMLDivElement} */
let element;
/**
 * 현재 로케일에 대한 Intl.DateTimeFormat의 인스턴스를 생성합니다.
 * @param {Intl.DateTimeFormatOptions} [formatOptions] - 사용 가능한 옵션에 대해서는 https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/DateTimeFormat를 참조하세요.
 * @returns {Intl.DateTimeFormat}
 */
const createDateTimeFormat = (formatOptions) =>
  Intl.DateTimeFormat(getPreferredLocales(), formatOptions);

기존 유형 정의를 import('path/to/module')를 통해 가져오기

여기 Vue Test Utils Wrapper 변수를 주석 처리하는 방법에 대한 예시가 있습니다. 이들은 즉시 정의되지 않습니다:

/** @type {import('helpers/vue_test_utils_helper').ExtendedWrapper} */
let wrapper;
// ...
wrapper = mountExtended(/* ... */);
/** @type {import('@vue/test-utils').Wrapper} */
let wrapper;
// ...
wrapper = shallowMount(/* ... */);

주의:

import()네이티브 JSDoc 구문이 아닙니다, 하지만 많은 IDE와 도구에서 인식됩니다. 이 경우, 우리는 코드의 명확성을 높이고 IDE의 개발자 경험을 개선하는 것을 목표로 하고 있습니다.

JSDoc의 한계

위에서 언급했듯이, JSDoc은 제한된 어휘를 가지고 있습니다. 그리고 이를 사용하면 유형을 완전히 설명할 수 없습니다. 하지만 때때로 3rd 파티 라이브러리의 유형 정의를 사용하여 우리의 코드에 대한 유형 추론을 작동하게 할 수 있습니다. 다음은 그런 접근법의 예입니다:

- export const mountExtended = (...args) => extendedWrapper(mount(...args));
+ import { compose } from 'lodash/fp';
+ export const mountExtended = compose(extendedWrapper, mount);

여기서 우리는 compose 함수의 TypeScript 유형 정의를 사용하여 mountExtended 함수에 추론된 유형 정의를 추가하고 있습니다. 이 경우 mountExtended 인수는 mount 인수와 같은 유형이 됩니다. 그리고 반환 유형은 extendedWrapper 반환 유형과 동일합니다.

우리는 여전히 JSDoc 구문을 사용하여 함수에 설명을 추가할 수 있습니다. 예를 들어:

/** 컴포넌트를 마운트하고 이를 위한 확장된 래퍼를 반환합니다. */
export const mountExtended = compose(extendedWrapper, mount);

시스템 요구 사항

IDE와 도구에서 GitLab 코드베이스 및 3rd 파티 패키지의 유형 정의가 올바르게 표시되기 위해 설정이 필요할 수 있습니다.

VS Code 설정

VS Code IntelliSense가 작동하는 데 문제가 있는 경우, TS 서버가 사용할 수 있는 메모리 양을 늘려야 할 수 있습니다. 이를 위해 settings.json 파일에 다음을 추가하세요:

{
    "typescript.tsserver.maxTsServerMemory": 8192,
    "typescript.tsserver.nodePath": "node"
}

별칭

우리의 코드베이스는 여러 가져오기에 대해 많은 별칭을 사용합니다. 예를 들어, import Api from '~/api';app/assets/javascripts/api.js 파일을 가져옵니다. 하지만 IDE는 그 별칭을 알지 못할 수 있으며, 따라서 Api의 유형을 알지 못할 수 있습니다. 대부분의 IDE에서 이를 수정하려면 jsconfig.json 파일을 생성해야 합니다.

GitLab 프로젝트에는 웹팩 구성과 현재 환경 변수를 기반으로 jsconfig.json 파일을 생성할 수 있는 스크립트가 있습니다. jsconfig.json 파일을 생성 또는 업데이트하려면 — GitLab 프로젝트 루트에서 다음을 실행하세요:

node scripts/frontend/create_jsconfig.js

jsconfig.json 파일은 gitignore 목록에 추가되므로, 이를 생성하거나 변경해도 GitLab 프로젝트에서 Git 변경 사항이 발생하지 않습니다. 이는 Git 풀에도 포함되지 않으므로, 수동으로 생성하거나 업데이트해야 합니다.

3rd 파티 TypeScript 정의

점점 더 많은 라이브러리가 유형 정의를 위해 TypeScript를 사용하고 있지만, 일부는 여전히 JSDoc 주석이 달린 타입이나 아예 타입이 없을 수 있습니다. 이러한 갭을 메우기 위해 TypeScript 커뮤니티는 인기 있는 JavaScript 라이브러리의 독립형 유형 정의를 생성하고 지원하는 DefinitelyTyped 이니셔티브를 시작했습니다. 우리는 해당 유형 패키지를 명시적으로 설치(yarn add -D "@types/lodash")하거나, 일부 언어 서비스에서 제공되는 자동 유형 획득(ATA) 기능을 사용하여 이러한 정의를 사용할 수 있습니다.

자동 유형 획득(ATA)은 DefinitelyTyped 목록에서 유형 정의를 자동으로 가져옵니다. 하지만 ATA가 작동하려면 전 세계적으로 설치된 npm이 필요할 수 있습니다. IDE는 npm 실행 파일의 위치를 설정하기 위한 대체 구성 옵션을 제공할 수 있습니다. 자세한 내용은 IDE 문서를 참조하세요.

ATA가 작동할 것이라는 보장은 없으며 Lodash는 우리의 유틸리티 함수의 많은 기반이므로 우리는 package.jsondevDependenciesDefinitelyTyped definitions for Lodash를 명시적으로 추가했습니다. 이를 통해 모든 사용자가 기본적으로 lodash 기반 함수의 유형 힌트를 받도록 보장합니다.