Как динамически построить тип из нескольких свойств?

Я изучаю расширенные типы в Typescript, пока что у меня есть type x, который принимает общий аргумент props extends string. Если значение для props равно name | age | email, это создает следующий тип:

{
  name: "name",
  age: "age",
  email: "email"    
}

Вышеупомянутый тип генерируется следующим определением типа:

type x<Property extends string> = { [K in Property]: K }

Также у меня есть конструктор типа для указанного выше типа:

let x = <props extends string>(p: props): x<props> => (<x<props>>{[p]: p})

Я могу создать тип, просто передав свойство типа string функции x:

x("name") // Type is x<"name">

Я бы хотел передать несколько свойств x таким образом, чтобы: x("name", "age", "email") создавал тип x<"name" | "age" | "email">.

Я знаю, что могу добиться этого, реорганизовав метод x следующим образом:

let x = <props extends string>(...p: props[]): x<props> => null! // Don't know how to implement this

Но я не знаю, как построить конкретный объект из этого ввода, аналогично (<x<props>>{[p]: p}), но перебрать все свойства, переданные в x.

Итак, есть ли способ перебирать свойства и динамически создавать тип?


person Harry Stylesheet    schedule 19.03.2019    source источник


Ответы (1)


Вам просто нужно перебрать реквизиты и назначить их новому объекту:

type x<Property extends string> = { [K in Property]: K }


let x = <props extends string>(...p: props[]): x<props> => {
    let result = {} as x<props>
    for (let k of p) {
        result[k] = k
    }
    return result;
} 

let a = x("name", "age") // Type is x<"name">
console.log(a);

Не существует магического синтаксиса для создания этого объекта в одном выражении, поэтому мы используем утверждение типа, чтобы солгать компилятору, что {} есть x<props>, и исправить объект в for после. Иногда необходимы утверждения типа, это один из тех случаев, когда у нас есть особая информация, которой нет у компилятора (информация, которую мы превратим из {} в действительный x<props> в следующих двух строках)

Или более лаконичная однострочная версия:

let x = <props extends string>(...p: props[]): x<props> => p.reduce((o, k) => (o[k] = k, o), {} as x<props>);
person Titian Cernicova-Dragomir    schedule 19.03.2019
comment
Тогда я мог бы также просто использовать props.foreach(prop => ...), это то, что это школьный проект для курса программной инженерии. Учителя не любят использовать циклы, ifs, else, for, while и т. Д. Поэтому иногда я не знаю, что разрешено. Но я буду, как будто петли в порядке - person Harry Stylesheet; 19.03.2019
comment
@HarryStylesheet, если вы хотите поразить их, используйте уменьшенную версию: P - person Titian Cernicova-Dragomir; 19.03.2019
comment
Я думаю, что это понравится, весь курс посвящен созданию функциональных композиций, функторов, моноидов и монад. - person Harry Stylesheet; 19.03.2019
comment
@HarryStylesheet нет ... для этого вам нужно написать императивный код в TS, за это нет очков: P. - person Titian Cernicova-Dragomir; 19.03.2019