Динамический вывод универсального типа для литералов объекта в TypeScript - PullRequest
0 голосов
/ 24 мая 2018

В машинописном тексте я могу объявить универсальную функцию следующим образом:

const fn: <T>(arg: T)=>Partial<T>

В этом случае TypeScript может иногда выводить параметр типа функции на основе фактических параметров, которые я ей передаю.Существует ли аналогичный способ определения универсального литерала объекта, параметр типа которого может быть динамически выведен на основе его содержимого?Что-то вроде:

interface XYZ { 
  obj: <T>{ arr: T[], dict: Partial<T> }
}

Я знаю, что могу сделать весь интерфейс универсальным, например, так:

interface XYZ<T> {
  arr: T[],
  dict: Partial<T>
}

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

const x: XYZ

не будет работать.Если я хочу сделать объявление общим, я вынужден написать:

const x: XYZ<any>

, но это не позволяет TypeScript динамически выводить конкретный универсальный тип на основе фактического содержимого x

Ответы [ 2 ]

0 голосов
/ 24 мая 2018

Ах, вам нужны общие значения, как описано в Microsoft / TypeScript # 17574 .Как вы заметили, они не существуют в языке, кроме как в случае универсальных функций.Вы можете указать issue на эту проблему, если хотите, или обсудить свой вариант использования, если считаете, что это полезно.

Учитывая общий интерфейс

interface XYZ<T> {
  arr: T[],
  dict: Partial<T>
}

Я бы просто использовал этот обходной путь:Создайте обобщенную функцию, чтобы проверить, что значение равно XYZ<T> для некоторого T, и разрешить вывод типа для фактического вывода T всякий раз, когда это необходимо.Никогда не пытайтесь объявлять что-то типа XYZ.Вот так:

const asXYZ = <T>(xyz: XYZ<T>) => xyz;

const x = asXYZ({
  arr: [{ a: 1, b: 2 }, { a: 3, b: 4 }],
  dict: { a: 1300 }
}); // becomes XYZ<{a: number, b: number}>

Вышесказанное обычно работает для меня на практике.Плюс в том, что это "естественный" TypeScript.Дело в том, что он не представляет «Мне все равно, какой тип T» правильно.


Если вы действительно хотите, вы можете определить экзистенциальный тип ,TypeScript изначально не поддерживает их, но есть способ представить это:

interface SomeXYZ {
  <R>(processXYZ: <T>(x: XYZ<T>) => R): R
}
const asSomeXYZ = <T>(xyz: XYZ<T>): SomeXYZ => 
  <R>(processXYZ: <T>(x: XYZ<T>) => R) => processXYZ(xyz);

Тип SomeXYZ - это конкретный тип, который больше не заботится о T, но содержит ссылкуXYZ<T> для некоторые T. Вы используете asSomeXYZ для создания объекта из объекта:

const someXYZ: SomeXYZ = asSomeXYZ({
  arr: [{ a: 1, b: 2 }, { a: 3, b: 4 }],
  dict: { a: 1300 }
}); // SomeXYZ

И используете его, передавая функцию, которая обрабатывает удерживаемую ссылку.Эта функция должна быть готова для XYZ<T> для любого T, так как вы не знаете, какой тип T a SomeXYZ удерживает.

// use one
const xyzArrLength = someXYZ((xyz => xyz.arr.length))

xyzArrLength являетсяnumber, поскольку функция xyz => xyz.arr.length возвращает number независимо от того, что T.

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

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

РЕДАКТИРОВАТЬ: перечитывание вашего вопроса заставляет меня думать, что вы на самом деле спрашиваете ответ, который я назвал «обходным путем».Так что ... использовать это?Приветствия.

0 голосов
/ 24 мая 2018
interface MyGenericObjectLiteral<T> {
  arr: T[],
  dict: Partial<T>
}

interface XYZ { 
    obj: MyGenericObjectLiteral<any>
}

Интерфейс XYZ здесь не будет общим, просто подобъект MyGenericObjectLiteral.На самом деле это именно то, что вы хотели, за исключением того, что он имеет немного другой синтаксис.

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