Объединение Reflection API с декораторами дает нам мощную комбинацию.

Чтобы в полной мере использовать декораторы TypeScript, необходимо понимать API Reflection и Reflection Metadata. Последний позволяет хранить данные в метаданных объекта, чтобы декораторы могли работать вместе для создания сложного поведения.
Рефлексия в разработке программного обеспечения — это способность кода смотреть на себя, исследовать или анализировать свои структуры данных, а также изменять свою собственную структуру и поведение во время выполнения. Но это было много громких слов и недостаточно практического понимания.
Эта статья призвана стать разумным и обоснованным введением в использование Reflection и API метаданных Reflection в TypeScript вместе с декораторами TypeScript. Другими словами, мы возьмемся за код, чтобы понять, что означают эти громкие слова. Многое из того, что здесь есть, полезно и в чистом JavaScript.
JavaScript Reflection API существует уже несколько лет, впервые появившись в Node.js версии 6.0. Большинству из нас, вероятно, не нужно его использовать, и, возможно, они не знают об этих API. Но Reflection и расширение Reflect Metadata очень полезны, помимо прочего, для реализации декораторов TypeScript.
Основной API Reflection фокусируется на изучении данных о свойствах и других аспектах объектов JavaScript. Документация Mozilla описывает его как предоставление методов для перехватываемой операции JavaScript, что является более громкими словами, которые на самом деле не помогают нашему пониманию.
- Документация:
Методы Reflection API привязаны к объекту Reflect. Этот объект просто существует, и вы не делаете ничего особенного, чтобы он существовал. У него нет конструктора, он просто существует и содержит статические функции (методы), которые можно использовать в коде для выполнения действий, связанных с большими словами, показанными выше. А именно, основной API Reflection позволяет коду извлекать данные об объектах и манипулировать этими данными или структурой объекта.
API метаданных Reflection — это предлагаемое дополнение к API Reflection, добавляющее дополнительные функциональные возможности метаданных. Он должен быть тесно связан с предложением декоратора и, как ожидается, будет стандартизирован после стандартизации декораторов.
- Документация:
Использование этого API требует небольшой настройки. Сначала вы устанавливаете пакет reflect-metadata, а затем добавляете import 'reflect-metadata' в свой код. Пакет содержит полифилл, добавляющий API метаданных к объекту Reflect.
Эта статья является частью серии:
- Знакомство с декораторами
- Декораторы класса
- Декораторы недвижимости
- Декораторы аксессуаров
- Декораторы параметров
- Декораторы методов
- Гибридные декораторы
- Использование Reflection и Reflection API с декораторами Эта статья
- Проверка данных во время выполнения с помощью декораторов
Настройка API метаданных Reflection для TypeScript
Чтобы включить эти API в TypeScript вместе с декораторами, см. инструкции в разделе Глубокое введение в использование и реализацию декораторов TypeScript.
Включение API метаданных Reflection требует установки пакета reflect-metadata следующим образом:
$ npm install reflect-metadata --save
После этого добавьте это в свой код:
import "reflect-metadata";
В вашем файле tsconfig.json сделайте следующие настройки:
{
"compilerOptions": {
...
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
...
}
}
Первый, experimentalDecorators, включает поддержку декоратора. Второй, emitDecoratorMetadata, выдает данные, требуемые пакетом reflect-metadata. Этот пакет позволяет нам делать мощные вещи в декораторах, записывая метаданные о классах, свойствах, методах и параметрах.
Использование Reflection API для получения данных об объектах JavaScript
Давайте начнем с использования Reflection API для получения данных об объектах.
Первый используемый метод, Reflect.has, позволяет проверить, имеет ли объект свойство с заданным именем. Чтобы понять, что это значит, начните с этого класса:
class HasExample {
year: number;
#privateYear: number;
#title: string;
set title(nt: string) { this.#title = nt; }
get title() { return this.#title; }
// set privateYear(ny: number) { this.#privateYear = ny; }
// get privateYear() { return this.#privateYear; }
area(x: number, y: number) {
return x * y;
}
constructor(privateYear: number) {
this.#privateYear = privateYear;
}
}
В этом определении класса есть несколько вещей, относительно которых мы можем выполнять запросы. Например, закрытые поля, такие как #title, имеют определенные ограничения доступа. Чтобы проверить это, добавьте этот код:
const hs = new HasExample(2222);
console.log(`year ${Reflect.has(hs, 'year')}`);
hs.year = 2022;
console.log(`year ${Reflect.has(hs, 'year')}`);
console.log(`privateYear ${Reflect.has(hs, 'privateYear')}`);
console.log(`title ${Reflect.has(hs, 'title')}`);
console.log(`area ${Reflect.has(hs, 'area')}`);
console.log(`xyzzy ${Reflect.has(hs, 'xyzzy')}`);
Запустив это, мы получим следующий вывод:
$ npx ts-node lib/reflection/has.ts year false year true privateYear false title true area true xyzzy false
Свойство year изначально не существует, но как только мы устанавливаем значение, оно существует. Мы видели такое поведение при изучении декораторов свойств, где мы увидели, что Object.hasOwnProperty возвращает false, пока мы не присвоим значение свойству.
Ибо #privateYear, несмотря на то, что ему присвоено значение через constructor, является частным свойством, к которому мы просто не можем получить доступ. Поэтому неудивительно, что Reflect.has возвращает false для этого поля.
Далее идет Reflect.ownKeys для получения имен свойств, Reflect.getOwnPropertyDescriptor для получения дескриптора свойства и Reflect.getPrototypeOf для получения прототипа объекта. Каждый метод параллелизирует в Object для получения одних и тех же данных. Добавьте этот код в скрипт:
console.log({
ownKeys: Reflect.ownKeys(hs),
keys: Object.keys(hs)
});
console.log({
ownProperty: Reflect.getOwnPropertyDescriptor(hs, "year"),
objectProperty: Object.getOwnPropertyDescriptor(hs, "year")
});
console.log({
ownProperty: Reflect.getOwnPropertyDescriptor(hs, "title"),
objectProperty: Object.getOwnPropertyDescriptor(hs, "title")
});
console.log({
reflectPrototype: Reflect.getPrototypeOf(hs),
objectPrototype: Object.getPrototypeOf(hs),
prototype: HasExample.prototype
});
Это показывает эквивалентность между некоторыми методами Reflect и Object. При запуске мы получаем такой вывод:
{ ownKeys: [ 'year' ], keys: [ 'year' ] }
{
ownProperty: { value: 2022, writable: true, enumerable: true, configurable: true },
objectProperty: { value: 2022, writable: true, enumerable: true, configurable: true }
}
{ ownProperty: undefined, objectProperty: undefined }
{ reflectPrototype: {}, objectPrototype: {}, prototype: {} }
Мы получаем одни и те же данные по каждому каналу.
Определение, получение, установка и удаление свойств (CRUD) с использованием отражения
Принцип CRUD обычно применяется к приложениям, использующим базу данных. Но, используя методы Reflection, мы можем создать свойство, прочитать значение свойства, обновить значение свойства и удалить свойство. Другими словами, мы можем выполнять операции CRUD над объектами JavaScript, используя Reflection.
Давайте начнем с этого простого объекта и вызовем несколько методов для чтения его начального состояния:
const example = {
prop1: "property1",
prop2: 42
};
console.log({
ownKeys: Reflect.ownKeys(example),
keys: Object.keys(example)
});
console.log('prop1', Reflect.get(example, 'prop1'));
console.log('prop2', Reflect.get(example, 'prop2'));
console.log(example);
Мы создали анонимный объект и использовали несколько методов для чтения ключей свойств объекта и их значений. Метод Reflect.get эквивалентен использованию аксессора get.
Результат:
{ ownKeys: [ 'prop1', 'prop2' ], keys: [ 'prop1', 'prop2' ] }
prop1 property1
prop2 42
{ prop1: 'property1', prop2: 42 }
У нас явно есть prop1 и prop2, и значения соответствуют ожидаемым. Это демонстрирует несколько способов чтения данных из объекта с помощью Reflection.
Чтобы создать свойство, запустите этот метод:
Reflect.defineProperty(example, 'prop3', {
value: "Property #3",
enumerable: true,
writable: true,
configurable: true
});
Это похоже на Object.defineProperty и определяет свойство объекта с заданным именем и заданным дескриптором свойства. В этом случае свойство называется prop3, а значение отображается в дескрипторе. Настройки для writable, enumerable и configurable управляют его поведением.
console.log({
ownKeys: Reflect.ownKeys(example),
keys: Object.keys(example)
});
console.log('prop3', Reflect.get(example, 'prop3'));
console.log(example); console.log(Object.getOwnPropertyDescriptors(example));
Давайте снова Прочитаем значения и убедимся, что свойство действительно было создано:
{
ownKeys: [ 'prop1', 'prop2', 'prop3' ],
keys: [ 'prop1', 'prop2', 'prop3' ]
}
prop3 Property #3
{ prop1: 'property1', prop2: 42, prop3: 'Property #3' }
{
prop1: {
value: 'property1',
writable: true,
enumerable: true,
configurable: true
},
prop2: { value: 42, writable: true, enumerable: true, configurable: true },
prop3: {
value: 'Property #3',
writable: true,
enumerable: true,
configurable: true
}
}
Он действительно есть, отображается в ключах свойств, дескрипторах свойств и с ожидаемым значением. Если enumerable равно false, оно не будет отображаться в ключах свойств и не будет отображаться в простом дампе объекта.
Чтобы обновить свойство, мы можем использовать обычный JavaScript:
example['prop3'] = 'NEW AND IMPROVED Property #3';
console.log('prop3', Reflect.get(example, 'prop3'));
console.log(example);
Reflect.set(example, 'prop3', 'ULTIMATELY IMPROVED Property #3');
console.log('prop3', Reflect.get(example, 'prop3'));
console.log(example);
Почему мы использовали example['prop3'], а не example.prop3? Компилятор TypeScript выполняется во время компиляции, а Reflect.defineProperty выполняется во время выполнения, поэтому компилятор не может знать, что prop3 будет существовать, и поэтому компилятор выдаст ошибку компиляции.
Использование example['prop3'] прекрасно работает в этом случае. Мы можем либо использовать обычное присваивание JavaScript для обновления свойства, либо использовать Reflect.set для обновления свойства.
Вывод таков:
prop3 NEW AND IMPROVED Property #3 {
prop1: 'property1',
prop2: 42,
prop3: 'NEW AND IMPROVED Property #3'
}
prop3 ULTIMATELY IMPROVED Property #3 {
prop1: 'property1',
prop2: 42,
prop3: 'ULTIMATELY IMPROVED Property #3'
}
Здесь мы показываем, что свойство обновляется таким образом. Если вы установите writable на false, свойство не будет изменено.
Последнее, что нужно сделать, это продемонстрировать удаление свойств.
Потому что Reflect.deleteProperty возвращает Boolean, указывающее, сработало ли удаление или нет. Значение атрибута configurable свойства определяет, можно ли его удалить.
{ ownKeys: [ 'prop2' ], keys: [ 'prop2' ] }
{ prop2: 42 }
{
prop2: { value: 42, writable: true, enumerable: true, configurable: true }
}
Собственно, это единственное оставшееся имущество.
Мы можем использовать обычный код JavaScript или Reflection для создания, чтения, обновления или удаления свойств объекта JavaScript.
Использование метаданных Reflection для хранения/чтения/обновления/удаления дополнительных данных об объектах TypeScript
Метаданные в данном случае означают данные, описывающие структуру или типы данных объектов JavaScript. Используя этот API, мы также можем хранить данные в виде метаданных, которые полностью отделены от фактических данных, хранящихся в объекте. Позже мы будем использовать метаданные вместе с парой декораторов для очень интересной возможности.
Существует достаточно методов метаданных отражения, чтобы также реализовать операции CRUD. Начнем здесь:
import 'reflect-metadata';
class MetadataExample {
prop1: string = 'prop1';
prop2: number = 42;
}
const example2 = new MetadataExample();
console.log({
ownMetadataKeys: Reflect.getOwnMetadataKeys(example2),
metadataKeys: Reflect.getMetadataKeys(example2)
});
Это создает простой класс, создает экземпляр класса, затем считывает некоторые данные о классе. Важно не забыть импортировать reflect-metadata. Невыполнение этого требования приводит к этой ошибке:
error TS2339: Property 'getOwnMetadataKeys' does not exist on type 'typeof Reflect'.
Как только вы убедитесь, что этот пакет импортирован, вывод будет таким:
$ npx ts-node lib/reflection/metadata.ts
{ ownMetadataKeys: [], metadataKeys: [] }
Другими словами, пока нет ключей метаданных. Но какие они?
Reflect.defineMetadata('metaProp1', 'prop1', example2);
console.log({
ownMetadataKeys: Reflect.getOwnMetadataKeys(example2),
metadataKeys: Reflect.getMetadataKeys(example2)
});
Для этого нужно создать свойство метаданных с ключом для metaProp1, значением для prop1 в объекте example2.
Другими словами, ключ метаданных — это имя свойства метаданных. Это похоже на то, что обычный ключ объекта — это имя обычного свойства объекта JavaScript.
Мы также можем определить метаданные для свойства объекта:
Reflect.defineMetadata('metaKey1ForProp1', 'data stored in prop1',
example2, 'prop1');
console.log({
prop1OwnMetadataKeys: Reflect.getOwnMetadataKeys(example2, 'prop1'),
prop1MetadataKeys: Reflect.getMetadataKeys(example2, 'prop1')
});
Здесь мы определили metaKey1ForProp1 для example2.prop1 и извлекаем ключи метаданных для этого свойства.
{
prop1OwnMetadataKeys: [ 'metaKey1ForProp1' ],
prop1MetadataKeys: [ 'metaKey1ForProp1' ]
}
Другая функция позволяет нам определить, существуют ли свойства метаданных:
console.log({
hasMetadata: Reflect.hasMetadata('metaProp1', example2),
hasOwnMetadata: Reflect.hasOwnMetadata('metaProp1', example2),
prop1NotHasMetadata: Reflect.hasMetadata('metaProp1', example2, 'prop1'),
prop1NotHasOwnMetadata: Reflect.hasOwnMetadata('metaProp1', example2, 'prop1'),
prop1HasMetadata: Reflect.hasMetadata('metaKey1ForProp1', example2, 'prop1'),
prop1HasOwnMetadata: Reflect.hasOwnMetadata('metaKey1ForProp1', example2, 'prop1'),
});
Это показывает, как мы используем hasMetadata и hasOwnMetadata для запроса свойств метаданных как для example2, так и для example2.prop1.
{
hasMetadata: true,
hasOwnMetadata: true,
prop1NotHasMetadata: false,
prop1NotHasOwnMetadata: false,
prop1HasMetadata: true,
prop1HasOwnMetadata: true
}
Два, которые показывают false, потому что мы использовали неправильное значение ключа метаданных.
Мы можем Прочитать значения метаданных следующим образом:
console.log({
metadata: Reflect.getMetadata('metaProp1', example2),
ownMetadata: Reflect.getOwnMetadata('metaProp1', example2),
prop1NotMetadata: Reflect.getMetadata('metaProp1', example2, 'prop1'),
prop1NotOwnMetadata: Reflect.getOwnMetadata('metaProp1', example2, 'prop1'),
prop1Metadata: Reflect.getMetadata('metaKey1ForProp1', example2, 'prop1'),
prop1OwnMetadata: Reflect.getOwnMetadata('metaKey1ForProp1', example2, 'prop1'),
});
С помощью getMetadata и getOwnMetadata мы читаем текущее значение свойства метаданных.
{
metadata: 'prop1',
ownMetadata: 'prop1',
prop1NotMetadata: undefined,
prop1NotOwnMetadata: undefined,
prop1Metadata: 'data stored in prop1',
prop1OwnMetadata: 'data stored in prop1'
}
И это вернуло ожидаемые значения. Для двух средних мы снова передали неверный ключ метаданных и в результате получили undefined.
Чтобы обновить значение метаданных, мы снова вызываем defineMetadata:
Reflect.defineMetadata('metaProp1', 'NEW IMPROVED prop1', example2);
Reflect.defineMetadata('metaKey1ForProp1', 'NEW IMPROVED prop1 on prop1', example2, 'prop1');
И, перезапустив последний пример, мы получим такой вывод:
{
metadata: 'NEW IMPROVED prop1',
ownMetadata: 'NEW IMPROVED prop1',
prop1NotMetadata: undefined,
prop1NotOwnMetadata: undefined,
prop1Metadata: 'NEW IMPROVED prop1 on prop1',
prop1OwnMetadata: 'NEW IMPROVED prop1 on prop1'
}
Наконец, мы можем удалить ключи метаданных, используя deleteMetadata:
Reflect.deleteMetadata('metaProp1', example2); Reflect.deleteMetadata('metaKey1ForProp1', example2, 'prop1');
Повторно запуская пример, мы получаем этот вывод:
{
metadata: undefined,
ownMetadata: undefined,
prop1NotMetadata: undefined,
prop1NotOwnMetadata: undefined,
prop1Metadata: undefined,
prop1OwnMetadata: undefined
}
Значения метаданных теперь исчезли.
Использование декоратора Reflect.metadata в классе TypeScript
Пакет reflect-metadata также предоставляет декоратор, который мы можем использовать для добавления значений метаданных к вещам. Он используется следующим образом:
import 'reflect-metadata';
@Reflect.metadata('decoratedClass', 'value')
class MetadataDecoratorExample {
// apply metadata via a decorator to a method (property)
@Reflect.metadata('decoratedMethod', 'method value')
method(param1?: number, param2?: string): string {
return 'Hello, World!';
}
}
const mde = new MetadataDecoratorExample();
Это просто устанавливает значение метаданных. Чтобы запросить значения метаданных, мы можем добавить это:
console.log({
classClassKeys: Reflect.getMetadataKeys(MetadataDecoratorExample),
classClassMetadata: Reflect.getMetadata('decoratedClass', MetadataDecoratorExample),
classKeys: Reflect.getMetadataKeys(mde),
methodKeys: Reflect.getMetadataKeys(mde, 'method'),
methodReturn: Reflect.getMetadata('design:returntype', mde, 'method'),
methodParams: Reflect.getMetadata('design:paramtypes', mde, 'method'),
methodType: Reflect.getMetadata('design:type', mde, 'method'),
methodDecorated: Reflect.getMetadata('decoratedMethod', mde, 'method'),
});
Выход для этого:
{
classClassKeys: [ 'decoratedClass' ],
classClassMetadata: 'value',
classKeys: [],
methodKeys: [
'design:returntype',
'design:paramtypes',
'design:type',
'decoratedMethod'
],
methodReturn: [Function: String],
methodParams: [ [Function: Number], [Function: String] ],
methodType: [Function: Function],
methodDecorated: 'method value'
}
Первые две строки принадлежат декоратору, прикрепленному к классу. Мы видим, что здесь отображается ключ метаданных, а также значение, но не в том случае, если мы делаем тот же запрос для экземпляра класса.
Начиная с methodKeys, мы делаем запросы к методу экземпляра с именем method. Мы нашли ожидаемый ключ метаданных, decoratedMethod, а также эти три других. Мы понимаем, что эти три ключа метаданных автоматически предоставляются TypeScript при определенных обстоятельствах. Для ключа decoratedMethod мы получили ожидаемое значение.
Для трех ключей design, идущих по выходам и именам свойств, цель выглядит так:
design:returntype— документирует возвращаемый тип свойстваdesign:paramtypes— документирует типы параметров свойстваdesign:type- Документирует тип вещи как свойство
Использование метаданных отражения в реализации декоратора TypeScript
Поскольку API метаданных Reflection предназначен для использования с реализацией декоратора, давайте рассмотрим, как это может работать. В нашей статье о декораторах методов мы разработали пример, который использовал массив для хранения данных из декораторов параметров для использования в декораторе метода. Сам по себе декоратор параметров не может делать ничего, кроме записи некоторых данных. Однако декоратор метода может перехватить вызов метода и, например, изменить аргументы, что и было сделано в этом примере.
Давайте перепишем этот пример, чтобы вместо этого использовать метаданные отражения.
class DefaultExample {
@SetDefaults
volume(
z: number,
@ParamDefault<number>(10) x?: number,
@ParamDefault<number>(15) y?: number,
title?: string
) {
const ret = {
x, y, z, volume: x * y * z, title
};
console.log(`volume `, ret);
return ret;
}
}
Это определение класса из этого примера. Он использует декоратор ParamDefault для определения значения по умолчанию для необязательных параметров, для которых значение не указано. Декоратор SetDefaults устанавливает переопределяющую функцию для этого метода, которая использует данные, поступающие от декораторов ParamDefault, для предоставления значений по умолчанию для любых параметров, которые не были предоставлены вызывающей стороной.
import 'reflect-metadata'; const DEFAULTS = 'defaults';
Импортируем пакет reflect-metadata. Затем мы определяем DEFAULTS, чтобы он содержал строку, которую мы будем использовать для ключа метаданных, в котором будут храниться значения по умолчанию. Этот метод похож на то, что мы могли бы сделать в программировании на C, где мы используем #define для создания символических констант. Использование этого для ключей метаданных позволяет нам быть уверенными в последовательном использовании одной и той же ключевой строки.
function ParamDefault<T>(value: T) {
return (target: Object, propertyKey: string | symbol,
parameterIndex: number)=> {
const defaults = Reflect.getMetadata(DEFAULTS, target, propertyKey)
|| {};
defaults[parameterIndex] = value;
Reflect.defineMetadata(DEFAULTS, defaults, target, propertyKey);
}
}
Это переписанный декоратор ParamDefault. Сначала он получает существующее значение метаданных, если оно есть. Из вышеизложенного мы знаем, что если значение в настоящее время не существует, будет возвращено undefined, и в этом случае мы заменим пустой объект.
В результате значение метаданных DEFAULTS содержит то, что мы можем назвать разреженным массивом, содержащим значения по умолчанию. Только индексы, в которых используется декоратор ParamDefault, будут иметь значение по умолчанию.
Затем мы используем Reflect.defineMetadata для обновления DEFAULTS.
function SetDefaults(target: Object, propertyKey: string,
descriptor: PropertyDescriptor) {
const originalMethod = descriptor.value;
descriptor.value = function (...args: any[]) {
console.log(`SetDefaults before ${propertyKey}`, args);
const defaults = Reflect.getMetadata(DEFAULTS, target, propertyKey)
|| {};
for (const key of Object.keys(defaults)) {
let def = defaults[key];
if (typeof args[key] === 'undefined'
|| args[key] === null) {
args[key] = def;
}
}
console.log(`SetDefaults after substitution ${propertyKey}`, args);
const result = originalMethod.apply(this, args);
console.log(`SetDefaults after ${propertyKey}`, result);
return result;
}
}
Здесь у нас есть декоратор SetDefaults. Прочтите статью о декораторах методов, чтобы получить полное описание того, что происходит. Суть в том, что originalMethod — это фактическая функция. Мы определили функцию замены, чтобы переопределить эту функцию. Функция переопределения ищет любые значения по умолчанию, которые должны быть предоставлены, изменяет массив аргументов, а затем вызывает исходную функцию.
Для проверки у нас есть следующее:
const de = new DefaultExample();
console.log(de.volume(10));
console.log('----------------------');
console.log(de.volume(20, null, 20, "Second"));
console.log('----------------------');
console.log(de.volume(30, 30, null));
console.log('----------------------');
console.log(de.volume(40, 40, 50, "Fourth"));
Это пробует каждую комбинацию значений по умолчанию в нашем приложении. На выходе мы получаем:
SetDefaults before volume [ 10 ]
SetDefaults after substitution volume [ 10, 10, 15 ]
volume { x: 10, y: 15, z: 10, volume: 1500, title: undefined }
SetDefaults after volume { x: 10, y: 15, z: 10, volume: 1500, title: undefined }
{ x: 10, y: 15, z: 10, volume: 1500, title: undefined }
----------------------
SetDefaults before volume [ 20, null, 20, 'Second' ]
SetDefaults after substitution volume [ 20, 10, 20, 'Second' ]
volume { x: 10, y: 20, z: 20, volume: 4000, title: 'Second' }
SetDefaults after volume { x: 10, y: 20, z: 20, volume: 4000, title: 'Second' }
{ x: 10, y: 20, z: 20, volume: 4000, title: 'Second' }
----------------------
SetDefaults before volume [ 30, 30, null ]
SetDefaults after substitution volume [ 30, 30, 15 ]
volume { x: 30, y: 15, z: 30, volume: 13500, title: undefined }
SetDefaults after volume { x: 30, y: 15, z: 30, volume: 13500, title: undefined }
{ x: 30, y: 15, z: 30, volume: 13500, title: undefined }
----------------------
SetDefaults before volume [ 40, 40, 50, 'Fourth' ]
SetDefaults after substitution volume [ 40, 40, 50, 'Fourth' ]
volume { x: 40, y: 50, z: 40, volume: 80000, title: 'Fourth' }
SetDefaults after volume { x: 40, y: 50, z: 40, volume: 80000, title: 'Fourth' }
{ x: 40, y: 50, z: 40, volume: 80000, title: 'Fourth' }
Как видите, в каждом случае были подставлены правильные значения.
Использование метаданных отражения значительно упростило этот пример. Использование внешнего массива, как мы сделали, не является чистым решением.
Краткое содержание
TypeScript имеет функцию для значений параметров по умолчанию. Таким образом, то, что мы можем создавать декораторы для внедрения значений по умолчанию, не является большим революционным достижением. Что является потрясающим, так это возможность с помощью нескольких строк кода реализовать функцию этого последствия. Многие языки не имеют функции значения параметра по умолчанию, а также не имеют возможности модифицировать такую функцию. Используя пару декораторов TypeScript и около 50 строк кода, мы смогли реализовать заслуживающую доверия полезную функцию.
В следующей статье мы пойдем дальше и продемонстрируем разработку автоматической проверки данных во время выполнения с помощью декораторов TypeScript.
об авторе
Дэвид Херрон: Дэвид Херрон — писатель и инженер-программист, занимающийся вопросами разумного использования технологий. Его особенно интересуют экологически чистые энергетические технологии, такие как солнечная энергия, энергия ветра и электромобили. Дэвид почти 30 лет работал в Силиконовой долине над программным обеспечением, начиная от систем электронной почты и заканчивая потоковым видео и языком программирования Java, и опубликовал несколько книг о программировании Node.js и электромобилях.
Первоначально опубликовано на https://techsparx.com.
Дополнительные материалы на PlainEnglish.io. Подпишитесь на нашу бесплатную еженедельную рассылку новостей. Подпишитесь на нас в Twitter и LinkedIn. Присоединяйтесь к нашему сообществу Discord.