on
JSDoc 및 VScode를 사용한 자바스크립트의 유형 안전성
JSDoc 및 VScode를 사용한 자바스크립트의 유형 안전성
TypeScript는 타입 안전 기능을 제공하는 널리 사용되는 트랜스파일 언어 중 하나이지만 타입스크립트 자체는 타입 안전뿐만 아니라 전체 자바스크립트 커뮤니티의 이점을 얻을 수 있다.
이 기사는 JSDoc, TypeScript 및 VScode를 사용하여 자바스크립트 프로젝트 유형을 안전하게 만드는 방법을 소개하는 것을 목표로 한다. 프로젝트를 더욱 강력하게 수행할 수 있을 뿐만 아니라 이러한 기술을 통해 DX를 향상시킬 수 있습니다. 전제는 여러분이 유형이 부담이라고 생각하지 않는다는 것입니다.
이 문서에서는 다음에 대해 다룹니다.
형식 정의에 JSDoc 태그를 일반적으로 사용합니다.
다른 파일에서 가져오기를 통해 형식을 재사용하는 방법.
컨버터를 사용하여 데이터를 효율적으로 입력하는 방법
VScode에서 정적으로 체크를 입력하고 tsc를 사용하여 시간 체크를 컴파일하는 방법.
이 문서에서는 다음 사항에 대해 다루지 않습니다.
JavaScript 또는 TypeScript 유형입니다.
JavaScript 또는 TypeScript 유형의 시스템 작동 방식입니다.
원시형
/** @type {string} */ const str = 'string'; /** @type {number} */ const num = 123; /** @type {boolean} */ const bool = true; /** @type {null} */ const nul = null; /** @type {undefined} */ const und = undefined; /** @type {symbol} */ const sym = Symbol('foo'); /** @type {*} */ const jsDocAny = 'any value'; /** @type {any} */ const tsAny = 'any value';
유형 객체
개체, 배열 및 함수를 포함한 개체 값, 함수에 대해서는 나중에 설명하겠습니다.
객체 값
/** * JSDoc style * @typedef {object} Rgb * @property {number} red * @property {number} green * @property {number} blue */ /** @type {Rgb} */ const color = { red: 255, green: 255, blue: 255 }; /** * TypeScript style * @typedef { brand: string; color: Rgb } Car */ /** @type {Car} */ const car = { brand: 'Some Brand', color: { red: 255, green: 255, blue: 255 }, };
배열 값
/** * JSDoc style * @type {Array.} */ const colors1 = [{ red: 0, green: 0, blue: 0 }]; /** * TypeScript style * @type {Rgb[]} */ const color2 = [{ red: 111, green: 111, blue: 111 }]; /** * TypeScript style * @type {Array} */ const color3 = [{ red: 255, green: 255, blue: 255 }];
유형함수
/** * JSDoc style named function type * @callback Add * @param {number} x * @param {number} y * @returns {number} */ /** @type {Add} */ const add = (x, y) => x + y; /** * TypeScript style inline function type * @typedef {(x: number, y: number) => number} TsAdd */ /** @type {TsAdd} */ const tsAdd = (x, y) => x + y; /** * JSDoc style type function with function declaration * @param {number} x * @param {number} y * @returns {number} */ function addDec(x, y) { return x + y; }
선택적 매개 변수
/** * JSDoc style optional parameter * @param {number} [x] optional * @param {number=} y number or undefined * @param {number} [z=1] optional with default (default not show in type hint) */ function jsDocOptional(x, y, z) {}
휴지 매개 변수
/** * JSDoc style rest parameter * @param {...number} num * @returns {number} */ function sum(...num) { return num.reduce((s, v) => s + v, 0); } /** * TypeScript style rest parameter * @param {number[]} num */ function tsSum(...num) { return num.reduce((s, v) => s + v, 0); }
반송형식
/** * No explicit return value * @returns {void} */ function noReturn() { console.log('no explicit return'); } /** * Function never return * @returns {never} */ function neverReturn() { throw Error('ERRORRRRR'); }
클래스 유형 및 이 항목
class Computer { /** * @readonly Readonly property * @type {string} */ CPU; /** * _clock type automatic infer from default value * @private Private property */ _clock = 3.999; /** * @param {string} cpu * @param {number} clock */ constructor(cpu, clock) { this.CPU = cpu; this._clock = clock; } /** * @param {string} cpu * @returns {void} */ change(cpu) { // @ts-expect-error this.CPU = cpu; // can not reasign readonly } } /** * Class is both value and type * @type {Computer} */ const computer = new Computer('Foo', 2.999); /** * @this {HTMLInputElement} * @returns {void} */ function handleChange() { console.log(`The input element's value is ${this.value}`); } document.querySelector('input').addEventListener('change', handleChange);
리터럴 값 입력
/** * Specify string type * @typedef {'RED'|'GREEN'|'BLUE'} RgbLabel */ /** @type {RgbLabel} */ const label = 'BLUE'; /** * Enumerate values type * @enum {number} */ const Status = { on: 1, off: 0, }; /** @type {Status} */ const off = Status.on;
고급 유형
일부 고급 유형을 주목할 필요가 있습니다.
유니언 타입
/** * Union type with pipe operator * @typedef {Date | string | number} MixDate */ /** * @param {MixDate} date * @returns {void} */ function showDate(date) { // date is Date if (date instanceof Date) date; // date is string else if (typeof date === 'string') date; // date is number else date; }
교차로 종류
/** * @typedef {Object} Foo * @property {string} foo */ /** * @typedef {Object} Bar * @property {string} bar */ /** @typedef {Foo & Bar} MixFooBar */ /** @type {MixFooBar} */ const mix = { foo: 'foo', bar: 'bar' };
캐스트
/** * Force value to some type with cast * Don't forget the parenthesis */ const foo = /** @type { foo: string } */ (JSON.parse('{ "foo": "bar" }'));
템플릿 및 조건부 유형
템플리트 및 조건부 유형은 라이브러리 작성자가 더 많이 사용하고, 입력을 보다 유연하게 만듭니다.
템플릿(일반 유형)
/** * @template T * @param {T} data * @returns {Promise} * @example signature: * function toPromise(data: T): Promise */ function toPromise(data) { return Promise.resolve(data); } /** * Restrict template by types * @template {string|number|symbol} T * @template Y * @param {T} key * @param {Y} value * @returns { [K in T]: Y } * @example signature: * function toObject(key: T, value: Y): { [K in T]: Y; } */ function toObject(key, value) { return { [key]: value }; }
조건형
/** * @template {string | number} T * @param {T} data * @returns {T extends string ? number : string} * @example signature: * function convert(data: T): T extends string ? number : string */ function convert(data) { return typeof data === 'string' ? Number(data) : String(data); }
재사용(가져오기) 유형
모든 파일에 입력할 필요는 없으며 다른 파일에서 가져오면 형식을 다시 사용할 수 있습니다.
/** * Reuse type by import JSDoc type definition from other file * @type {import('./object-types').Rgb} */ const rgb = { red: 0, green: 0, blue: 0 }; /** * Import type from d.ts file * @type {import('./pokemon').Pokemon} */ const pikachu = { name: 'Pikachu', attack: 55, speed: 90 }; /** * Import type from node_modules * Make sure install `@types/express` first * @type {import('express').RequestHandler} * @example signture: * const handler: e.RequestHandler> */ const handler = async (req, rep) => { const body = req.body; rep.status(200).json({ message: 'OK', body }); };
효율적인 타이핑 방법
d.ts 파일에 형식 쓰기
TypeScript 구문에 입력하는 것이 JSDoc에 비해 더 간단하고 효율적입니다. .d.ts 파일에서 데이터 형식을 정의하고 import(. /path )를 사용할 수 있습니다.유형을 가져오려면 입력한 다음 JSDoc을 입력합니다.
// color.d.ts export interface Rgb { red: number; green: number; blue: number; } export interface Rgbs extends Rgb { alpha: number; } export type Color = Rgb | Rbgs | string;
// here the equivalent types define in JSDocs syntax // its much more verbose /** * @typedef {object} Rgb * @property {number} red * @property {number} green * @property {number} blue */ /** * @typedef {object} Alpha * @property {number} alpha */ /** @typedef {Rgb & Alpha} Rgba */ /** @typedef {Rgb | Rgba | string} Color */
// color.js import type from color.d.ts /** @type {import('./color').Color} */ const color = { red: 255, green: 255, blue: 255, alpha: 0.1 };
확실히 입력된 것을 잊지 마세요.
TypeScript를 사용하지 않더라도 모든 데이터나 함수를 직접 정의할 필요는 없지만, Definally Type에서 제공하는 유형 정의를 사용할 수 있습니다.
예를 들어, JavaScript에서 express.js로 Node.js API 응용프로그램을 개발하는 경우 @type/node 및 @type/express를 설치하는 것을 잊지 마십시오.
$ npm install -D @types/node @types/express
js 파일에서:
/** @type {import('express').RequestHandler} */ const handler = async (req, rep) => { // req and rep is now with type };
JSON 데이터를 유형으로 변환
API 응답 데이터를 입력해야 하는 라이브러리뿐만 아니라 이 프로세스를 보다 효율적으로 만드는 방법도 필요합니다.
JSON 형식의 응답 데이터로 복사한 다음 아래 도구를 사용하여 JSON을 유형으로 변환할 수 있습니다.
변환은 사용자가 JSDoc과 TypeScript 정의를 포함한 많은 출력 포맷으로 많은 소스 포맷을 변환하는 것을 도울 수 있는 온라인 변환기이다.
{ "red": 255, "green": 255, "blue": 255 }
위의 JSON 데이터는 JSDoc 정의로 변환할 수 있습니다.
/** @typedef {Object} json * @property {Number} blue * @property {Number} green * @property {Number} red */
또는 TypeScript 정의
export interface Root { red: number; green: number; blue: number; }
형식 이름을 변경하고 이 코드를 .js 또는 d.ts 파일에 붙여넣을 수 있습니다.
JSON to TS는 VScode의 확장 기능으로 JSON 데이터를 TypeScript 정의로 변환하는 데 도움이 될 수 있습니다.
이 확장의 주요 장점은 중첩된 JSON 데이터를 처리할 수 있다는 것입니다. 그러나 현재 transform.tools를 사용할 수 없습니다.
유형 검사를 활성화하는 방법
데이터와 기능을 이미 입력했더라도 실수해도 VScode는 경고나 오류 메시지를 제공할 수 없습니다.
VScode에서 파일별 또는 프로젝트 폴더별로 유형 확인을 사용하도록 설정하는 두 가지 옵션은 모두 수동으로 사용하도록 설정해야 합니다.
파일별 확인
지정 파일에 대한 형식 검사를 활성화하려면 설명 // @ts-체크 파일을 첫 번째 줄에 추가합니다.
// @ts-check // @ts-expect-error /** @type {string} */ const name = 123;
파일별 유형 검사를 활성화하면 프로젝트의 유형 안전을 점진적으로 향상시키는 데 매우 유용합니다.
프로젝트 폴더별 확인
각 파일에 대해 수동으로 설정하는 대신 jsconfig.json을 사용하여 전체 프로젝트에 대한 유형 확인을 설정할 수 있습니다.
프로젝트 폴더의 루트에 jsonconfig.json 파일을 수동으로 만들거나 아래 명령을 실행하여 tsconfig.json을 생성한 다음 jsonconfig.json으로 이름을 변경할 수 있습니다.
$ npx typescript --init
또는 유형 스크립트를 전체적으로 설치한 후 다음 명령을 사용할 수 있습니다.
$ npm install -g typescript $ tsc --init
그런 다음 tsconfig.json의 이름을 jsconfig.json으로 변경합니다.
파일을 열면 많은 옵션이 표시되며 대부분의 옵션은 기본적으로 비활성화되어 있습니다.
두려워하지 마십시오. "JavaScript 지원" 옵션을 수정하고 원본 경로를 명시적으로 지정하기만 하면 됩니다.
{ "compilerOptions": { "checkJs": true, "maxNodeModuleJsDepth": 1 }, "input": ["src"] }
소스 폴더 아래에 자바스크립트 파일을 생성했다가 실수하면 VScode에서 경고를 제공합니다.
/** @type {string} */ const foo = 123; // Error: Type 'number' is not assignable to type 'string'.
유형 확인을 위한 설정 명령
많은 파일로 프로젝트가 클 수 있으므로 각 파일을 열어 모든 파일이 안전한지 확인하는 것은 거의 불가능합니다. 우리는 더 똑똑하고 빠른 방법이 필요하다.
패키지의 스크립트 속성 아래에 있습니다.json 파일, 다음과 같은 명령을 생성합니다.
{ "scripts": { "check": "tsc --project jsconfig.json", "check:watch": "tsc --watch --project jsconfig.json" } }
이제 check 명령을 한 번만 실행하고 check:watch 명령을 실행하여 원본 경로의 파일이 변경되었을 때 계속 다시 확인할 수 있습니다.
$ npm run check $ npm run check:watch
요약
JSDoc, TypeScript 및 VScode를 활용하여 정적 유형 검사와 컴파일 시간 확인의 이점을 모두 얻을 수 있습니다. JavaScript 프로젝트를 개발 중이더라도 타협할 필요가 없습니다.
질문이 있는 경우 아래에 의견을 달거나 위에서 언급한 리포지토리로 이동하여 문제를 제출하십시오.
from http://gong-tech.tistory.com/12 by ccl(A) rewrite - 2021-09-22 05:27:49