release

3.5

Вспомогательный тип Omit

В повседневной разработке очень часто требуется определить новый тип описывающий некоторую часть существующего. До версии TypeScript 3.5 подобное можно было реализовать при помощи типов Exclude<T, U> и Pick<T, K>.

ts
type Person = {
    firstName: string;
    lastName: string;

    age: number;
};

/**
 * определяем тип Union состоящий из строковых литеральных типов
 * представляемых ключами типа Person, за исключением исключенного
 * поля age.
 *
 * type RemainingKeys = "firstName" | "lastName"
 */
type RemainingKeys = Exclude<keyof Person, 'age'>;

/**
 * определяем новый тип состоящий из ключей
 * полученных на предыдущем шаге.
 *
 * type PersonName = {
 *  firstName: string;
 *  lastName: string;
 * }
 */
type PersonName = Pick<Person, RemainingKeys>;
```

Поскольку потребность в сужении типа возникает довольно часто, это вынуждает разработчиков выносить требующийся функционал в отдельную сущность, что неизбежно затрудняет чтение кода другими разработчиками, так как знакомый всем механизм скрыт за незнакомым им идентификатором типа (именем типа).

По этой причине разработчики `TypeScript` расширили стандартную библиотеку `lib.d.ts` новым вспомогательным типом `Omit<T, K>`, который стандартизирует, обсуждаемую на протяжении главы, логику.

`````ts
type Person = {
    firstName: string;
    lastName: string;

    age: number;
};

/**
 * type PersonName = {
 *  firstName: string;
 *  lastName: string;
 * }
 */
type PersonName = Omit<Person, 'age'>;
```

Улучшение механизма проверки избыточных полей в объедененных типах (Union types)

В TypeScript есть механизм называемый проверкой избыточности свойств (excess property checking), который призван выявлять опечатки в литералах объекта.

ts
type RectCssStyle = {
    width: string;
    height: string;

    color?: string;
};

let graphics: RectCssStyle = {
    width: '24px',
    height: '24px',

    colour: 'red', // Error
};
```

С этим механизмом связанно два неоднозначных момента. Первый момент заключается в том, что в _TypeScript_, в некоторых случаях, избыточность допускается. Один из таких случчаев является присвоение идентификатору, ассоциированного с типом Union, значения принадлежащего одновеременно ко всем типам определеляющих это объединение.

`````ts
// до версии v3.5

type CoordXY = {
    x: number;
    y: number;
};

type CoordZ = {
    z: number;
};

/**
 * По логике, в данном объекте, либо поля xy либо поле z являются излишними,
 * так как тип Union одновременно представляет лишь один определяющий
 * его тип данных
 */
const coords: CoordXY | CoordZ = {
    x: 0,
    y: 0,

    z: 0,
};
```

Второй неоднозначный момент сокрыт в реализации описанного выше поведения, которая даже не предусматривает проверку типов для полей являющихся излишними.

`````ts
// до версии v3.5

type CoordXY = {
    x: number;
    y: number;
};

type CoordZ = {
    z: number;
};

// вывод типов считает что значение принадлежит к типу CoordXY
const coordsFirst: CoordXY | CoordZ = {
    x: 0,
    y: 0,
    z: '', // Ok, несмотря на то, что тип определен как number при присваивании значения принадлежащего к типу string ошибки не возникает
};

// вывод типов считает что значение принадлежит к типу CoordZ
const coordsSecond: CoordXY | CoordZ = {
    x: '', // Ok...
    y: '', // Ok, несмотря на то, что тип определен как number при присваивании значения принадлежащего к типу string ошибки не возникает
    z: 0,
};
```

Это поведение кажется нелогичным, поэтому Начиная с версии _TypeScript_ `v3.5` частично было изменено. Теперь при проверке литералов объекта выполняется проверка типов, что устроняет второй неоднозначным случай.

`````ts
// начиная с v3.5

type CoordXY = {
    x: number;
    y: number;
};

type CoordZ = {
    z: number;
};

const coordsFirst: CoordXY | CoordZ = {
    x: 0,
    y: 0,
    z: '', // Error
};

const coordsSecond: CoordXY | CoordZ = {
    x: '', // Error
    y: '', // Error
    z: 0,
};
```

Тем не менее, нужно помнить, что первый случай, который также быд обозначен как не совсем очевидный, остался.

`````ts
// ...

const coords: CoordXY | CoordZ = {
    x: 0,
    y: 0,
    z: 0,
}; // Ok
```

Введение нового флага --allowUmdGlobalAccess

Посколько, до недавнего времени, спецификация ESMAScript не предлагала модульную систему, разработчикам приходилось прибегать к использованию множества несовместимых между собой реализаций поражденных сообществом, которые затем были унифицированы за счет так называемой универсальной модульной системы (UMD). Кроме того,библиотеки создавались таким образом чтобы быть доступными через глобальную область видимости.

Со стандартизацией esmodules, в TypeScript, при попытке обратится к библиотеке через объявленный в глобальной области идентификатор, возникает ошибка.

ts
import * as Rx from 'rxjs';

const ref = React.createRef(); // Error, обращение к глобальным переменным в модулях недопустимо
```

`````ts
import * as Rx from 'rxjs';
import * as React from 'react';

const ref = React.createRef(); // Ok, так как импортирован модуль
```

Но посколько со стороны сообщества неоднократно поступали просьбы сделать доступными в модулях глобальные определения, разработчики _TypeScript_ ввели новый флаг `--allowUmdGlobalAccess` благодаря которому появилась такая возможность.

`````ts
// tsconfig.json

{
  {
    "compilerOptions": {
      "allowUmdGlobalAccess": false
    }
  }
}
```

`````ts
import * as Rx from 'rxjs';

const ref = React.createRef(); // Ok, доступ к глобальному определению из модуля
```

Улучшение механизма проверки объединенных типов

ts
type Source = { done: boolean; value: number };
type Target = { done: false; value: number } | { done: true; value: number };

declare let source: Source;
declare let target: Target;

target = source; // Error, до v3.5 и Ok после
```

Поскольку в примере выше тип `Source`, за исключением поля `done`, идентичен типу `Target`, новый механизм проверки объедененных типов допускает присвоение значения первого типа идентификатору принадлежащего ко второму типу.

Улучшение механизма вывода типа высшего порядка для обобщенных конструкторов

В TypeScript версии v3.4 был усовершенствован механизм вывода типов для обобщенных функций реализующих функциональную композицию. Это в свою очередь повысило эффективность использования функциональных-hoc при разработке React-приложений. С выходом версии v3.5 подобный механизм был перенесен на конструкторы классов, что также повысило эффективность вывода типов при использования их в качестве классовых-hoc.

ts
type ComponentClass<P> = new (props: P) => Component<P>;
declare class Component<P> {
    props: P;
    constructor(props: P);
}

type HocProps = { hocProp: string };
declare function hoc<WrappedProps>(
    WrappedComponent: ComponentClass<WrappedProps>
): ComponentClass<WrappedProps & HocProps>;

type NestedProps<T> = { nestedProp: T };
class CustomComponent<T> extends Component<NestedProps<T>> {}

/**
 * <v3.5
 * const WrappedComponent: ComponentClass<NestedProps<{}> & HocProps>
 *
 * >=v3.5
 * const WrappedComponent: new <T>(props: NestedProps<T> & HocProps) => Component<NestedProps<T> & HocProps>
 *
 */
const WrappedComponent = hoc(CustomComponent);
```

Неявное ограничение обобщенных параметров типом unknown

Начиная с версии v3.5 обобщенные параметры типа неявно определяются как принадлежащие к типу unknown.

ts
declare class Stack<T> {
    getItem(): T;
}

/**
 * <v3.5
 * let item: {}
 *
 * >= v3.5
 * let item: unknown
 */
let stack = new Stack();
let item = stack.getItem();
```

Изменение правил для типа unknown и индексной сигнатуре

В TypeScript тип представленный индексной сигнатурой ассоциированной с типом any, совместим с любым объектным типом.

ts
type Dictionary = { [key: string]: any };

let dictionary: Dictionary;

class SomeClass {}
const literalObjectType = { f0: 0, f1: '' };
const anyType: any = 5;

dictionary = {}; // Ok
dictionary = literalObjectType; // Ok
dictionary = anyType;
dictionary = new SomeClass(); // Ok
dictionary = () => {}; // Ok
dictionary = Boolean; // Ok
```

До версии `v3.5` тип `unknown` в индексной сигнатуре вел себя аналогичным образом.

`````ts
// <v3.5

type Dictionary = { [key: string]: unknown };

let dictionary: Dictionary;

class SomeClass {}
const literalObjectType = { f0: 0, f1: '' };
const anyType: any = 5;

dictionary = {}; // Ok
dictionary = literalObjectType; // Ok
dictionary = anyType;
dictionary = new SomeClass(); // Ok
dictionary = () => {}; // Ok
dictionary = Boolean; // Ok
```

Начиная с версии `v3.5` поведение для типа `unknown` в индексной сигнатуре было изменено. Теперь он совместим лишь с типами представляемыми литералами объектов и типом `any`.

`````ts
// >= 3.5

type Dictionary = { [key: string]: unknown };

let dictionary: Dictionary;

class SomeClass {}
const literalObjectType = { f0: 0, f1: '' };
const anyType: any = 5;

dictionary = {}; // Ok
dictionary = literalObjectType; // Ok
dictionary = anyType;
dictionary = new SomeClass(); // Error
dictionary = () => {}; // Error
dictionary = Boolean; // Error
```

Изменение логики для Object.keys для версии es5

Начиная с версии es6 метод Object.keys при передави в качестве аргументов значения не принадлежащему к типу object возвращает пустой массив. Тем не менее в аналогичной ситуации в версии es5 возникает исключение. Поэтому начиная с версии v3.5 поведение для метода Object.keys при передачи в него аргументов не совместымых с типом object было изменено.

ts
/**
 * tsconfig.json
 * "target": "es5" or\and "lib": ["es5"]
 *
 * v<3.5 is Ok, let keys: string[]
 * v>=3.5 is Error
 */
let keys = Object.keys(50);
```