Проблема в том, что вы хотите вручную указать один параметр типа, а компилятор выводит другой. Это называется вывод аргумента частичного типа , и у TypeScript его нет (по состоянию на TS3.4). Вы можете либо вручную указать все параметры типа (что вы не хотите делать), либо иметь все параметры типа, выведенные компилятором (что вы не можете сделать, потому что нет ничего, из чего вы могли бы вывести указанный тип).
В этой ситуации есть два основных обходных пути:
Первый - полностью полагаться на вывод типа и использовать фиктивный параметр для вывода типа, который вы обычно указываете. Например:
class GenericCLass<T, K extends keyof T> {
keyNameFromAnotherType: K;
// add dummy parameter
constructor(dummy: T, keyNameFromAnotherType: K) {
this.keyNameFromAnotherType = keyNameFromAnotherType;
}
}
const test = new GenericCLass(null! as InterfaceWithProperties, 'stringProperty');
// inferred as GenericCLass<InterfaceWithProperties, "stringProperty">
Вы можете видеть, что значение, переданное в качестве первого параметра, просто null
во время выполнения, и конструктор все равно не смотрит на него во время выполнения. Но системе типов было сказано, что она имеет тип InterfaceWithProperties
, что достаточно для того, чтобы тип выводился так, как вы хотите.
Другой обходной путь - разбить все, что обычно использует частичный вывод, на две части с помощью curry ; первая универсальная функция позволит вам указать один параметр, и она возвращает универсальную функцию (или универсальный конструктор в данном случае), которая выводит другой параметр. Например
// unchanged
class GenericCLass<T, K extends keyof T> {
keyNameFromAnotherType: K;
constructor(keyNameFromAnotherType: K) {
this.keyNameFromAnotherType = keyNameFromAnotherType;
}
}
// curried helper function
const genericClassMaker =
<T>(): (new <K extends keyof T>(
keyNameFromAnotherType: K
) => GenericCLass<T, K>) =>
GenericCLass;
// specify the one param
const InterfaceWithPropertiesGenericClass =
genericClassMaker<InterfaceWithProperties>();
// infer the other param
const test = new InterfaceWithPropertiesGenericClass('stringProperty');
// inferred as GenericCLass<InterfaceWithProperties, "stringProperty">
Это оставляет ваше определение класса в покое, но создает новую вспомогательную функцию, которая возвращает частично указанную версию конструктора GenericClass
для использования вами. Вы можете сделать это одним выстрелом, но это уродливо:
const test = new (genericClassMaker<InterfaceWithProperties>())('stringProperty');
// inferred as GenericCLass<InterfaceWithProperties, "stringProperty">
Во всяком случае, надеюсь, что одна из этих работ для вас. Удачи!