Здесь вас блокируют несколько вещей.
Во-первых, порядок свойств в типе объекта в настоящее время ненаблюдаемый в TypeScript, потому что они не влияют на возможность назначения. Например, в системе типов нет разницы между типом {a: string, b: number}
и {b: number, a: string}
. Есть грязные уловки, которые можно использовать, чтобы извлечь информацию из компилятор, чтобы увидеть, представляет ли он ключи как ["a", "b"]
против ["b", "a"]
, но все, что он делает, это дает вам некоторый порядок при компиляции; при чтении объявления типа сверху вниз не гарантируется, что порядок будет одинаковым при каждой компиляции! См. microsoft / TypeScript # 17944 для открытой проблемы, требующей согласованного упорядочивания. . И см. microsoft / TypeScript # 42178, где приведен пример, казалось бы, несущественного изменения кода, которое меняет заказ от того, что ожидается. На данный момент мы не можем каким-либо согласованным образом автоматически преобразовать тип объекта в упорядоченный кортеж.
Во-вторых, имена аргументов в типе функции намеренно недоступны как строковые литералы. Они ненаблюдаемы по тем же причинам, что и упорядочение свойств объекта: они не влияют на возможность назначения. Например, нет разницы между типом функции (foo: string) => void
и (bar: string) => void
в системе типов. Я даже не думаю, что есть какие-то уловки, которые могли бы раскрыть такие подробности. В системе типов имена аргументов функций используются только в качестве документации или IntelliSense. Вы можете преобразовать аргументы именованной функции в помечены элементы кортежа, но не более того. См. этот комментарий в microsoft / TypeScript # 28259, где объясняется, что мы можем: • использовать имена параметров сигнатуры вызовов или метки кортежей в качестве строк в TypeScript. На данный момент мы не можем автоматически превратить помеченный кортеж в тип объекта, где ключи объекта соответствуют меткам кортежа.
Вы можете как бы обойти обе эти проблемы, если вместо попытки преобразовать объект в кортеж или кортеж в объект, мы предоставим достаточно информации, чтобы сделать и то, и другое:
const userOptionKeys = ["param1", "param2", "thirdOne"] as const;
type PositionalUserOptions = [string, number, boolean];
Здесь userOptionKeys
- явный упорядоченный список желаемых ключей в UserOptions
объектах ... в том же порядке, что и в нашем вручную созданном кортеже PositionalUserOptions
. Теперь, когда у нас есть имена ключей, мы можем построить UserOptions
:
type UserOptions = { [I in Exclude<keyof PositionalUserOptions, keyof any[]> as
typeof userOptionKeys[I]]: PositionalUserOptions[I] }
/* type UserOptions = {
param1: string;
param2: number;
thirdOne: boolean;
} */
И пока мы это делаем, мы можем написать функцию для преобразования кортежа типа PositionalUserOptions
в объект типа UserOptions
(с утверждением типа any
, чтобы компилятор не пытался его проверить, чего он не может сделать. без труда):
function positionalToObj(opts: PositionalUserOptions): UserOptions {
return opts.reduce((acc, v, i) => (acc[userOptionKeys[i]] = v, acc), {} as any)
}
Теперь мы можем написать этот User
класс, используя positionalToObj
в реализации конструктора для нормализации вещей:
class User {
constructor(...args: PositionalUserOptions);
constructor(options: UserOptions);
constructor(...args: [UserOptions] | PositionalUserOptions) {
const opts = args.length === 1 ? args[0] : positionalToObj(args);
console.log(opts);
}
}
new User("a", 1, true);
/* {
"param1": "a",
"param2": 1,
"thirdOne": true
} */
Оно работает! С точки зрения системы типов и возможности присваивания это лучшее, что вы можете сделать. С точки зрения документации / IntelliSense это не очень хорошо. Когда вы вызываете new User()
, в документации для многопараметрической версии вам будут предоставлены такие ярлыки, как args_0
и args_1
. Если вы хотите, чтобы это были param1
и param2
, вам просто нужно укусить пулю и дважды написать имена параметров; один раз в виде строковых литералов и снова в виде меток кортежей, поскольку нет возможности преобразовать один в другой:
const userOptionKeys = ["param1", "param2", "thirdOne"] as const;
type PositionalUserOptions = [param1: string, param2: number, thirdOne: boolean];
Стоит ли оно того? Может быть ... решать тебе.
Ссылка для игровой площадки на код
person
jcalz
schedule
11.02.2021