TypeScript

Введение

TypeScript — это сильно типизированный, объектно-ориентированный и компилируемый open-source язык, разработанный и поддерживаемый компанией Microsoft. Он был создан в целях привнесения статических типов в современный JavaScript. Компилятор TypeScript читает код TypeScript с объявлениями и аннотациями типов и выдает чистый и читаемый JavaScript-код с преобразованными и удаленными конструкциями. Этот код запускается в любой среде исполнения ECMAScript, например, в браузерах и Node.js.

В этой статье мы ознакомимся с несколькими функциями, добавленными в TypeScript в различных версиях 3.x.

Версии TypeScript:

Разберем новые функции в хронологическом порядке:

Ссылки на проект

Эта новая концепция появилась в TypeScript 3.0. С ее помощью можно установить один проект TypeScript в зависимость от другого проекта TypeScript с помощью ссылки на файлы tsconfig.json, что увеличивает модульность написания кода. TypeScript 3.0 также предоставляет новый режим для tsc, флаг --build. Он работает с ссылками на проекты, ускоряя сборку.

Новый тип unknown

В TypeScript 3.0 также представлен новый тип верхнего уровня unknown. Любой элемент может быть обозначен типом unknown, однако этот тип нельзя назначить для других элементов, кроме самого себя и any, без утверждения типа или сужения потока управления.

Поддержка defaultProps в JSX

Для разработки React на JSX TypeScript 3.0 предоставляет поддержку нового типа в пространстве имен JSX под названием LibraryManagedAttributes. Это вспомогательный тип, который определяет изменения в prop-типах компонентов перед использованием, что позволяет вносить изменения в props и mappings.

export interface Props {
    name: string;
}

export class Greet extends React.Component<Props> {
    render() {
        const { name } = this.props;
        return <div>Hello {name.toUpperCase()}!</div>;
    }
    static defaultProps = { name: "world"};
}

// Проверка типов! Тип assertions не нужен!
let el = <Greet />

Однако стоит отметить, что свойства по умолчанию выводятся из типа property defaultProps. Поэтому при добавлении явной аннотации типа компилятор не сможет идентифицировать свойства по умолчанию.

Типы Mapped на кортежах и массивах

В TypeScript 3.1 типы объектов mapped теперь работают так же, как при итерации по кортежам и массивам. Это означает, что при использовании существующих типов mapped, таких как Partial или Required из lib.d.ts, они автоматически работают с кортежами и массивами. Благодаря этому TypeScript лучше выражает функции, подобные Promise.all.

type MapToPromise<T> = { [K in keyof T]: Promise<T[K]> };

type Coordinate = [number, number]

type PromiseCoordinate = MapToPromise<Coordinate>; // [Promise<number>, Promise<number>]

MapToPromise принимает тип T, и если этот тип является кортежем как Coordinate, то преобразуются только числовые свойства. В [number, number] есть два пронумерованных свойства: 0 и 1. При предоставлении подобного кортежа MapToPromise создает новый кортеж, в котором свойства 0 и 1 являются Promise оригинального типа. Таким образом, результирующий тип PromiseCoordinate приобретает тип [Promise<number>, Promise<number>].

Выбор версий

Это функция, появившаяся в версии TypeScript 3.1, с помощью которой как разработчик, так и компилятор, могут использовать новые функции и одновременно отслеживать используемые версии. При использовании разрешения модуля Node в TypeScript 3.1, когда TypeScript открывает файл package.json, чтобы выяснить, какие файлы нужно прочитать, он сначала просматривает новое поле typesVersions. package.json с полем typesVersions выглядит следующим образом:

{
  "name": "package-name",
  "version": "1.0",
  "types": "./index.d.ts",
  "typesVersions": {
    ">=3.1": { "*": ["ts3.1/*"] }
  }
}

Этот package.json говорит TypeScript о том, что нужно проверить, работает ли текущая версия TypeScript. Если это 3.1 или более поздняя версия, он определяет путь, импортированный относительно пакета, и считывает из папки пакета ts3.1.

strictBindCallApply

В JavaScript есть методы bind, call и apply, которые используются в функциях. С их помощью можно выполнять такие действия, как привязка this и частичное применение аргументов, вызов функции с различным значением для this и вызов функции с массивом для аргументов. Команде TypeScript потребовалось некоторое время, чтобы смоделировать эти функции, и все они первоначально использовали любое количество аргументов и возвращали любые значения.

В TypeScript 3.1 типы параметров объединены с концепцией списков параметров моделирования с типами tuple, чтобы обеспечить более строгую проверку bind, call и apply при использовании нового флага strictBindCallApply. При использовании этого флага методы в вызываемых объектах описываются новым глобальным типом CallableFunction, который объявляет более строгие версии сигнатур для bind, call и apply.

Это выглядит следующим образом:

function foo(a: number, b: string): string {
    return a + b;
}

let a = foo.apply(undefined, [10]);              // ошибка: слишком мало аргументов
let b = foo.apply(undefined, [10, 20]);          // ошибка: второй аргумент - это число
let c = foo.apply(undefined, [10, "hello", 30]); // ошибка: слишком много аргументов
let d = foo.apply(undefined, [10, "hello"]);     // все в порядке! возвращает строку

Таким образом, независимо от того, выполняется ли сложное метапрограммирование или используются простые шаблоны, такие как методы привязки в экземплярах классов, эта функция помогает обнаружить множество ошибок.

Поддержка BigInt

BigInt — это встроенный объект для представления целых чисел от 2 до 53, которое является наибольшим числом, представленное JavaScript с помощью примитива Number. В TypeScript 3.2 предусмотрена проверка типов для BigInts, а также поддержка выделения литералов BigInt при отслеживании esnext.

Синтаксис в TypeScript для нового примитивного типа называется bigint. bigint можно получить с помощью вызова функции BigInt() или литерала BigInt, добавив n в конец любого целочисленного литерала:

let foo: bigint = BigInt(100); // функция BigInt
let bar: bigint = 100n;        // литерал BigInt

// Здесь возвращаются целые числа, которые могут стать очень большими!
function fibonacci(n: bigint) {
    let result = 1n;
    for (let last = 0n, i = 0n; i < n; i++) {
        const current = result;
        result += last;
        last = current;
    }
    return result;
}

fibonacci(10000n)

Наследование tsconfig.json через пакеты Node.js

В TypeScript 3.2 tsconfig.json теперь может работать из node_modules. При использовании пустого пути для поля "extends" в tsconfig.json, TypeScript самостоятельно откроет пакеты node_modules.

{
    "extends": "@my-team/tsconfig-base",
    "include": ["./**/*"]
    "compilerOptions": {
        // Переопределение некоторых параметров для каждого проекта.
        "strictBindCallApply": false,
    }
}

Здесь TypeScript поднимается по папкам node_modules в поисках пакета @my-team/tsconfig-base. В каждом из этих пакетов TypeScript сначала проверяет, содержит ли package.json поле "tsconfig", а затем пытается загрузить файл конфигурации из этого поля. Если его не существует, то TypeScript попытается прочитать из tsconfig.json в root. Это схоже с процессом поиска файлов .js в пакетах, которые использует Node, и процессом поиска .d.ts, уже используемым TypeScript. Представьте, насколько это удобно для больших проектов.

const assertions

TypeScript 3.4 представляет новую конструкцию для литеральных значений, которая называется constутверждениями. Синтаксис является типом утверждения с const вместо названия типа (например, 123 as const). При создании новых литеральных выражений с constутверждениями можно сообщить языку, что массивы являются кортежами только для чтения или что литералы объектов получают свойства только для чтения.

// Type '"hello"'
let x = "hello" as const;

// Type 'readonly [10, 20]'
let y = [10, 20] as const;

// Type '{ readonly text: "hello" }'
let z = { text: "hello" } as const;

Проверка типов globalThis

Команда TypeScript также исправила проблемы получения доступа к значениям в глобальной области видимости. В новом TypeScript 3.4 появилась поддержка проверки типов для новой глобальной переменной globalThis в ECMAScript, которая указывает на глобальную область видимости. В отличие от других решений, globalThis предоставляет стандартный способ для получения доступа к глобальной области действия, который можно использовать в различных окружениях.

// в глобальном файле:

var abc = 100;

// Ссылается на 'abc' выше.
globalThis.abc = 200;

Примечание: глобальные переменные, объявленные с let и const, не обнаруживаются с globalThis.


Перевод статьи Nwose Lotanna: New Features in TypeScript You Didn’t Know Exist