Можно ли автоматически генерировать свойства класса из его универсального типа? - PullRequest
1 голос
/ 24 октября 2019

Есть ли способ в TypeScript генерировать класс со свойствами из его универсального типа, подобного следующему:

class SomeClass<T> {
  // Read props from T somehow here?
}

interface Props {
  a: number;
  b: boolean;
}

const obj = new SomeClass<Props>;

// So now both obj.a and obj.b are available in autocomplete with correct types from Props interface

interface Props2 {
  some: string;
  other: number;
  props: boolean;
}

const obj2 = new SomeClass<Props2>;
// Now only three props from Props2 are available to obj2

Я не хочу добавлять что-то вроде

class SomeClass {
  [prop: string]: any
}

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

Ответы [ 2 ]

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

То, что вы ищете здесь, является чем-то ближе к mixin , чем стандартное наследование. Но универсальные миксины из interface X<T> extends T сорта не поддерживаются TypeScript .

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

class _SomeClass<T> {
    constructor(t: T) {
        Object.assign(this, t);
    }
    // inside here we don't know about properties of T, though
    // so you'll have to do assertions
    get<K extends keyof T>(k: K): T[K] {
        return (this as any as T)[k]; // assertion
    }
}
type SomeClass<T> = T & _SomeClass<T>;
const SomeClass = _SomeClass as new <T>(t: T) => SomeClass<T>;

Класс _SomeClass<T> является универсальным, но сам по себе не реализует T. Конструктор присваивает свойства T объекту this (вам нужно что-то подобное этому назначению, чтобы получить действительный T во время выполнения). Внутри реализации _SomeClass<T> вам нужны утверждения, когда вы пытаетесь получить доступ к любым свойствам T на this (так что this as any as T или this as this & T ваши друзья).

Тогда тип SomeClass<T> определяется как пересечение из _SomeClass<T> и T, а затем значение SomeClass является просто конструктором _SomeClass, но мы утверждаем, чтоон действует как конструктор SomeClass<T>.

Давайте посмотрим, как он работает:

interface Props {
    a: number;
    b: boolean;
}

const obj = new SomeClass<Props>({ a: 1, b: true });
console.log(obj.a); // 1
console.log(obj.get("a")); // 1

interface Props2 {
    some: string;
    other: number;
    props: boolean;
}

const obj2 = new SomeClass<Props2>({ some: "", other: 2, props: false });

Те, которые компилируются с IntelliSense, как вы ожидаете.

Есть еще ограничения,курс. TypeScript действительно позволяет вам extends классов, где имена ключей известны статически. Так что если вам нужен общий подкласс из SomeClass, вам не повезет:

class Nope<T> extends SomeClass<T> { // error!
/* Base constructor return type 'SomeClass<T>' is 
 not an object type or intersection of object types 
 with statically known members. */
}

A бетон подкласс будет работать (до тех пор, пока keyof T известен статически):

class Okay extends SomeClass<Props> {

}

Хорошо, надеюсь, это поможет;удачи!

Ссылка на код

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

Сопоставленные типы нельзя использовать в классах. Но, построив ответ здесь: https://stackoverflow.com/a/48345383/626911,, вы можете создать функцию для создания объекта со свойствами, определенными для интерфейса, подобного этому:

interface Props {
  a: number;
  b: boolean;
}

function build<T>(value?: T): T {
  return Object.assign({}, value);
}


const obj = build<Props>();
// obj contains typed a and b properties

// Or, pass in default values
// const obj = build<Props>({ a: 2, b: false });    
...