Вручную расширить тип - PullRequest
1 голос
/ 07 июня 2019

Есть ли способ указать, что я хочу тип, который является расширенной версией другого типа?

Мой вариант использования выглядит примерно так:

function times<A extends number, B extends number>(a: A, b: B): A & B;

Идея состоит в том, что эта функция times сохранит все расширения на A и B, но само значение будет изменено на a * b.

Проблема в том, что если я позвоню times(2, 3), я получу тип возврата 2 & 3, который является неверным и невозможным. Мне не нужно, чтобы оно было 6, но я определенно не хочу, чтобы оно было 2 & 3, я хочу, чтобы оно было number в этом случае.

Расширение типов обычно происходит «автоматически» в Typescript, с различными тайными правилами. Обычно это вызывает у меня головную боль, но на этот раз я хочу этого, но здесь этого не происходит. Во многих подобных обстоятельствах я, конечно, не хотел бы этого, но именно здесь я хочу этого.

Итак, я хочу указать способ возврата WidenedVersionOf<A> & WidenedVersionOf<B>. Есть ли способ сделать такую ​​вещь?


Некоторое обоснование / ответ на ожидаемое «почему вы просто не используете композицию?»

Да, расширения для типов номеров здесь важны. Мне хорошо известно, что в большинстве случаев «оберточный» объект со значением, хранящимся в виде числа, и расширением, хранящимся отдельно, был бы «лучшим» способом сделать это. Это не в большинстве случаев. На самом деле мы используем фирменные примитивы для единиц измерения (например, секунды, футы). Это означает, что эти расширения фактически существуют только в домене типа и не присутствуют во время выполнения. Это имеет ряд преимуществ для нас.

У нас есть конечное количество брендов (первичные из них In, Per и Delta, поэтому мы можем иметь const v = 20 as number & In<'feet'> & Per<'second'> или const d = 100 as number & In<'feet'> & Delta). Функция times имеет большое количество перегрузок, чтобы охватить все эти случаи. Тем не менее, у нас также есть много универсальных контейнеров, которые хранят номера любого вида брендинга, используя N extends number. Именно в этом случае возникают проблемы с перегрузкой times, описанной выше. Невозможно (и, я думаю, невозможно), чтобы каждый из этих универсальных контейнеров обрабатывал отдельно все возможные варианты In, Per и / или Delta, которые могут появиться.

1 Ответ

2 голосов
/ 07 июня 2019

Вот мой лучший ответ на это, предполагая, что вы используете In<T>, как описано в вашем вопросе, и забывая о том факте, что пересечение In<A> & In<B> не будет действительно представлять тот же тип In<A*B>. Вместо этого вам понадобится другой способ взять A и B и получить тип, соответствующий A*B, который отслеживает мощности единиц и отмены и т. Д. Пересечение вряд ли сделает это. Вот в сторону, здесь:

class In<T> { private __v!: T };

function withUnit<N extends number, T extends string>(n: N, t: T): N & In<T> {
  return n as any;
}
const oneNewton = withUnit(1, "Newtons"); // 1 & In<"Newtons">
const oneMeter = withUnit(1, "meter"); // 1 & In<meter>

Это похоже на то, что ты делаешь, верно?

В любом случае, Widen ниже, в частности, будет работать только с типами формы number & In<T>. Насколько я знаю, нет способа перебрать пересечения, поэтому нельзя программно взять произвольный тип 5 & Foo & Bar & Baz и извлечь из него Bar, даже не зная о Bar:

type Widen<N extends number> = N extends In<infer T> ? number & In<T> : number;

А вот times как и просили:

declare function times<A extends number, B extends number>(a: A, b: B): Widen<A> & Widen<B>;

Что работает, как вы хотели для этих:

const six = times(2, 3); // number
const twoMeters = times(2, oneMeter); // number & In<"meter">
const alsoTwoMeters = times(oneMeter, 2); // number & In<"meter">;

Но причудливые вещи здесь происходят из-за вышеупомянутых предостережений:

const oneSquareMeterUhWait = times(oneMeter, oneMeter); // number & In<"meter">;    
const twoNewtonMetersUhWait = times(2, times(oneNewton, oneMeter)); 
// number & In<"Newtons" & "meter">

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

Надеюсь, это поможет; удачи!

Ссылка на код

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...