Rescrict предоставляет простые способы связывать большинство функций JavaScript таким образом, чтобы ощущаться как нативно, так и безопасно. Удобно, он даже обеспечивает @unwrap декоратор для параметрического полиморфизма. Тем не менее, есть несколько мест, где мы все еще должны заполнить пробелы. В этой статье документируется, как привязаться к функции JavaScript, которая может вернуть любой из нескольких различных типов, используя варианты резкости.
Необходимость в пользовательском решении
JavaScript является одновременно динамичным и слабо напечатанным, и даже стандартные библиотеки в полной мере используют эти функции способами, которые могут вызвать головные боли для тех, кто пытается использовать систему статического типа.
TypeScript имеет дело с этим очень буквально через типы союзов. То есть тип буквально определяется как OneType | Theothertype так что разработчик может учитывать оба случая. Резкгрипция не имеет типов союза, но имеет Варианты , который может быть абстракциями вокруг различные виды.
Под капотом это объекты JavaScript со свойствами, которые представляют базовые значения.
Образец вывода из официальной документации
var f1 = /* Child */0;
var f2 = {
TAG: /* Mom */0,
_0: 30,
_1: "Jane"
};
var f3 = {
TAG: /* Dad */1,
_0: 32
};
Это гладко на стороне резкости, но ненативен к JS. Это означает, что под текущей структурой варианта нет пути, чтобы напрямую связываться с таким методом, как Idbobjectstore.keypath , который может вернуться NULL строка или массив струн. Мы, безусловно, можем представлять аналогичный тип
IdbobjectStoreKeypath.res
type t = Null | String(string) | Array(Js.Array.t);
… но Rescrict ожидает, что экземпляры такого типа будут иметь Тег и пронумерованные свойства, такие как выборочный вывод JavaScript выше. Нам нужен способ классифицировать Что возвращается нашим привязкой, и соответствующим образом называют соответствующий конструктор варианта.
Написание привязки к фиктивному типу
Мы собираемся сделать немного небезопасной черной магии, которую мы не хотим, чтобы пользователи нашей библиотеки использовали, поэтому давайте завершим его в модуль, чтобы компенсировать его из кода, который мы разоблачим в нашем .Resi :
module Private = {
};
Как мы установили, нет никакого способа напрямую представлять возвращенное значение клавиатура В системе типа резкости, так что давайте не будем беспокоиться.
module Private = {
type any;
@get external keyPath: t => any = "keyPath";
};
Теперь давайте углубимся в уродливые вещи.
Думать о типах в JavaScript
Давайте на мгновение выйдем из резкости и подумаем о стороне выполнения JavaScript. Если бы мы управляли этим в JavaScript, мы бы, вероятно, использовали бы тип Оператор для возврата строки, а затем мы сможем соответствующим образом отразить нашу логику.
Но мы не можем только использовать тип Потому что тип нуля и typeof [] Оба возвращаются "объект" , так что нам также понадобится нулевая проверка.
Так что, если бы мы делали это в JavaScript, мы получим кусок кода что -то вроде
x => x === null ? "null" : typeof x
Давайте держимся за эту мысль.
Моделирование типа типа в резкости
Наше выражение Javascript выше (для всех idbobjectStoreKeypath s) вернет «null», «объект» или «строка». Это очень хорошо переводится на полиморфический вариант резкости, например, так:
type typeName = [#null | #"object" | #"string"];
Итак, теперь, с этим типом, мы можем ввести наше выражение JavaScript в %RAW JavaScript фрагмент:
type typeName = [#null | #"object" | #"string"]; let getType: any => typeName = %raw(`x => x === null ? "null" : typeof x`);
Итак, теперь мы можем получить клавиатура Через привязку, и тогда мы можем получить название типа этого клавиатуры. Мы так близки.
волшебным образом называть правильный конструктор
У нас есть последний шаг: нам нужно включить наш Typename Чтобы позвонить в переключение на нашем Typename , используйте Obj.magic Чтобы преобразовать наш тип в правильный тип резкости, а затем позвонить нашему конструктору, который завершит наш тип в наш вариант.
let classify = (v: any): IDBObjectStoreKeyPath.t =>
switch(v -> getType) {
| #null => IDBObjectStoreKeyPath.Null;
| #"object" => IDBObjectStoreKeyPath.Array(v -> Obj.magic);
| #"string" => IDBObjectStoreKeyPath.String(v -> Obj.magic);
};
Obj.magic Будет отдать ценность, чтобы вернуть все, что он делает, но наш Переключение должен убедиться, что актерский состав безопасен (на практике, хотя и не теоретически).
классификация любого клавиатуры
Связывая все это вместе, теперь мы можем использовать наш классифицировать функция дезинфицировать любой фиктивный тип возвращен из нашего клавиатура связывание.
let keyPath = (t: t): IDBObjectStoreKeyPath.t => t -> Private.keyPath -> Private.classify;
(Это та вещь, которая волнует меня по поводу функционального программирования- когда мы разбиваем вещи на достаточно маленькие кусочки, все кажется простым и простым.)
Завершая
Я надеюсь, что это был полезный ресурс для написания сложных привязков. Просто чтобы просмотреть, мы смогли успешно вернуть этот вариант …
IdbobjectStoreKeypath.res
type t = Null | String(string) | Array(Js.Array.t);
… из функции называется клавиатура Обертывая привязку, как так:
Idbobjectstore.res
type t;
module Private = {
type any;
@get external keyPath: t => any = "keyPath";
type typeName = [ #null | #"object" | #"string" ];
let getType: any => typeName = %raw(`x => x === null ? "null" : typeof x`);
let classify = (v: any): IDBObjectStoreKeyPath.t =>
switch(v -> getType) {
| #null => IDBObjectStoreKeyPath.Null;
| #"object" => IDBObjectStoreKeyPath.Array(v -> Obj.magic);
| #"string" => IDBObjectStoreKeyPath.String(v -> Obj.magic);
};
};
/* properties */
let keyPath = (t: t): IDBObjectStoreKeyPath.t =>
t -> Private.keyPath -> Private.classify;
Я надеюсь, что это было полезно для моделирования типов союзов с использованием вариантов резкости. Со своей стороны, я обязательно вернусь к этой статье, продолжая писать и итерацию на привязках.
Оригинал: “https://dev.to/webbureaucrat/binding-to-a-javascript-function-that-returns-a-variant-in-rescript-im”