Есть ли способ создать общий тип литерала в машинописи? - PullRequest
3 голосов
/ 11 октября 2019

Я хочу сделать что-то, что, вероятно, неортодоксально (и погранично бесполезно, если мы честны), так что мы идем:

Я хочу передать литерал в качестве параметра Generic, а затем создать его экземпляр. Рассмотрим следующий пример:

const log = console.log;

class Root<T = {}> {
  // public y: T = {}; // this obviously doesn't work

  // again this won't work because T is used a value. Even if it worked,
  // we want to pass a literal
  // public y: T = new T();

  public x: T;
  constructor(x: T) {
    this.x = x;
  }
}

class Child extends Root<{
  name: "George",
  surname: "Typescript",
  age: 5
}> {
  constructor() {
    // Duplicate code. How can I avoid this?
    super({
      name: "George",
      surname: "Typescript",
      age: 5
    });
  }

  foo() {
    // autocomplete on x works because we gave the type as Generic parameter
    log(`${this.x.name} ${this.x.surname} ${this.x.age}`); 
  }
}


const main = () => {
  const m: Child = new Child();
  m.foo();
};
main();

Это работает, но я должен передать литерал дважды. Один раз в общем для автозаполнения для работы и один раз в конструкторе для инициализации. Тьфу.

Еще один способ сделать это - объявить мой литерал за пределами Child. Например:

const log = console.log;

class Root<T = {}> {
  // public y: T = {}; // this obviously doesn't work

  // again this won't work because T is used a value. Even if it worked,
  // we want to pass a literal
  // public y: T = new T(); 

  public x: T;
  constructor(x: T) {
    this.x = x;
  }
}

// works but ugh..... I don't like it. I don't want to declare things outside of my class
const literal = {
  name: "George",
  surname: "Typescript",
  age: 5
}
class Child extends Root<typeof literal> {
  constructor() {
    super(literal);
  }

  foo() {
    // autocomplete on x works because we gave the type as Generic parameter
    log(`${this.x.name} ${this.x.surname} ${this.x.age}`); 
  }
}


const main = () => {
  const m: Child = new Child();
  m.foo();
};
main();

Есть ли какой-нибудь магический способ создания экземпляра универсального типа без явного предоставления его снова через конструктор?

Ответы [ 2 ]

2 голосов
/ 11 октября 2019

Вы можете использовать промежуточную оболочку, которая позаботится как о расширении родового объекта, так и вызове конструктора:

function fromRoot<T>(x: T) {
  return class extends Root<T> {
    constructor() {
      super(x)
    }
  }
}

, а затем:

class Child extends fromRoot({
  name: "George",
  surname: "Typescript",
  age: 5
}) { etc }

PG

0 голосов
/ 11 октября 2019

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

Кроме того, я не вижу смысла в классе Child, если он будет ограничен конкретным объектом - почему бы вам не определить type, которого ожидает ваш Child, а затем создать экземпляр для потомкас конкретным экземпляром этого типа?

type MyType = {
    name: string
    surname: string
    age: number
}

class Child extends Root<MyType> {
    foo() {
        // autocomplete on x works because we gave the type as Generic parameter
        console.log(`${this.x.name} ${this.x.surname} ${this.x.age}`);
    }
}

const child = new Child({
    name: "George",
    surname: "Typescript",
    age: 5
})

Если вы хотите повторно использовать этот конкретный Child, вы можете просто экспортировать этот конкретный child экземпляр.

Смотрите детская площадка .

...