Сделать файлы javascript осведомленными об их файлах определений машинописи

Машинопись позволяет писать .d.ts файлы определений для ваших локальных файлов JavaScript, например, так:

src/
  main.js
  imported.js
  imported.d.ts

main.js

import { func } from './imported';
console.log(func("1", "1"));

imported.js

export const func = (numVal, strVal) =>
  `The number is ${numVal}, the string is ${strVal}`;

imported.d.ts

export const func: (numVal: number, strVal: string) => string;

Это дает следующие ошибки, с опцией noImplicitAny:

src/imported.js(1,22): error TS7006: Parameter 'numVal' implicitly has an 'any' type.
src/imported.js(1,30): error TS7006: Parameter 'strVal' implicitly has an 'any' type.
src/main.js(3,18): error TS2345: Argument of type '"1"' is not assignable to parameter of type 'number'.

Последняя ошибка хороша, она мешает нам передать строку, когда мы должны были передать число в качестве первого параметра. Но для первых двух, в импортированном файле JavaScript, он не знает, что тип параметра был определен. Это предотвращает возможность использования noImplicitAny, но также предотвращает ошибки, когда вы, например, передаете numValue к функции, ожидающей строку.

Можно ли сделать файлы javascript осведомленными об их определениях внутри машинописного текста, предпочтительно без изменения исходного javascript.

1 ответ

Решение

Хотя javascript-файлы не могут быть осведомлены об их файлах определений, в Typescript 2.3 добавлена ​​поддержка проверки типов с использованием комментариев JSDoc.

Изменение проекта на:

src/
  main.js
  imported.js

imported.js

/**
 * @return {string} 
 * @param {number} numVal 
 * @param {string} strVal 
 */
export const func = (numVal, strVal) =>
  `The number is ${funcs.func3(numVal)}, the string is ${strVal}`;

main.js

import { func } from './imported';

/**
 * Works fine
 * @param {number} number 
 */
const mainFunc1 = (number) =>
  func(number, "Hello");      

/**
 * Argument of type 'string' is not assignable to parameter of type 'number'
 * @param {string} string 
 */
const mainFunc2 = (string) =>
  func(string, "Hello");

Теперь компилятор машинописи знает, что numVal это число, и strVal это строка Пытаясь пройти numVal к функции, которая не принимает число, в то время как приводит к ошибке. @return в func технически избыточен здесь, поскольку он знает, что возвращает строку (он знал бы это даже без JSDoc), но это хорошо для согласованности.

Хотя для этого требуется изменить исходный javascript, для этого используются только комментарии.

Ограничения

Это не имеет всех возможностей полной проверки типов Typescript, но это большая часть пути. Например, если у вас есть модуль, который возвращает объект, содержащий типизированные ключи:

/**
 * @param {number} num 
 */
const func1 = (num) => num * 2;

export default {
  func1,
}

А затем потреблять его с:

import imported from './module';

imported.func1(3);    // This is okay
imported.func1("3");  // This is an error
imported.func2(3);    // This is also okay, but it shouldn't be

С файлами javascript это не будет ошибкой, так как он не сгенерировал тип для экспорта по умолчанию, поэтому он не может подтвердить, что func2 не существует. В то время как в Typescript, он сказал бы вам Property 'func2' does not exist on type '{ func: (num: number) => number; }'. Did you mean 'func'?без необходимости явно объявлять тип экспорта по умолчанию.

Машинопись 2.5

В Typescript 2.5 также добавлена ​​поддержка утверждения типа с помощью JSDoc, например:

// Argument of type 'string | number' is not assignable to parameter of type 'number'.
const func1 = () =>
  func(funcReturnsNumberOrString(true), "Hi");

// Fine
const func2 = () =>
  func(/** @type {number} */ (funcReturnsNumberOrString(true)), "Hi");

/**
 * @returns { number | string }
 * @param {boolean} bool
 */
const funcReturnsNumberOrString = (bool) =>
  bool ? 2 : "2";

В этом случае мы знаем, что funcReturnsNumberOrString возвращает число, поэтому мы можем сказать Typescript как таковой.

Другие вопросы по тегам