Как вы получаете тип объекта, который клонируется из экземпляра класса? - PullRequest
1 голос
/ 17 марта 2020

Предположим, у меня есть этот пример класса, но на самом деле у него гораздо больше свойств

class Foo {
  name: string
  dob: number

  constructor(name: string, dob: number) {
    this.name = name;
    this.dob = dob;
  }

  get age() {
     return new Date().getTime() - this.dob
  }
}

Теперь Typescript умный, и когда я создаю экземпляр класса, он даст мне все нужные свойства:

var classInstance = new Foo('name', new Date().getTime())

classInstance.name // ok
classInstance.dob // ok
classInstance.age // ok

Где-то в моем коде класс клонируется с помощью оператора Spread, я не уверен, что TS делает за сценой, но он действительно умный и дает мне все нужные свойства

var classJSON = {...classInstance};

classJSON.name // ok
classJSON.dob // ok
classJSON.age // missing

tsplayground

Это здорово, однако мне иногда нужно использовать тип classJSON .. Единственный способ, которым я могу подумать, чтобы извлечь его, это сделать это:

var classJSON  = {...new Foo('', 0)}
type ClassJSONType = typeof classJSON; 

Есть ли способ извлечь тип прямо из Foo без необходимости Javascript для создания экземпляра?

Ответы [ 4 ]

3 голосов
/ 26 марта 2020

Если все, чего вы пытаетесь избежать, это инициализировать Foo, вы можете сделать что-то вроде этого:

var funRetClassJSON = () => ({ ...Foo.prototype });
type TypeOfClassJSON = ReturnType<typeof funRetClassJSON>;

или

var nullClassJSON = false as any ? null : ({ ...Foo.prototype });
type AltTypeOfClassJSON = NonNullable<typeof nullClassJSON>;

Конечно, оба эти примера по-прежнему связаны с некоторыми штраф за время выполнения, так как некоторые JS все еще генерируются.

2 голосов
/ 26 марта 2020

Отказ от ответственности: этот пост - мод, основанный на ответе Мэтта Маккатчена . Это решение solid, чистый TS без JS эффекта времени выполнения.

type IfEquals<X, Y, T> =
    (<T>() => T extends X ? 1 : 2) extends
  (<T>() => T extends Y ? 1 : 2) ? T : never;

type JSONify<T> = Pick<T, {
  [P in keyof T]: IfEquals<{ [Q in P]: T[P] }, { -readonly [Q in P]: T[P] }, P>
}[keyof T]>;

Приведенный выше трюк исключает все поля readonly. Для дальнейшего исключения методов используйте следующее:

type JSONify<T> = Pick<T, {
  [P in keyof T]: IfEquals<{ [Q in P]: T[P] extends Function ? never : T[P] }, { -readonly [Q in P]: T[P] }, P>
}[keyof T]>;

Детская площадка

2 голосов
/ 24 марта 2020

В настоящее время это невозможно, поскольку система типов TS не позволяет отслеживать чужие свойства. Существует предложение именно для того, что вы хотите. Этот комментарий также описывает возможные пути к express Оператору распространения без удаления собственных свойств.

0 голосов
/ 27 марта 2020

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

var classJSON = {...classInstance} as Foo;

Это напрямую сообщит Typescript, какой тип объекта должен быть. Вывод типа с помощью оператора распространения может быть невозможен без этой аннотации.

...