Аргумент _ModelClass
для createCollection
полностью не используется внутри JavaScript, верно? Похоже, что его единственная цель - предоставить компилятору TypeScript сайт вывода для параметра generi c T
. Если да, то, возможно, «правильным» решением будет полностью отказаться от этого аргумента:
function createCollection<T extends Model>(
options: CollectionOptions
): Collection<T> {
const collection: Collection<T> = new Collection(options);
return collection;
}
и либо заставить вызывающих абонентов вручную указать тип параметра T
, например, User
:
const model = createCollection<User>({ key: 'id' }); // manually specified
model.add(new User()); // okay
, или попросите их присвоить возвращаемое значение вручную аннотированной переменной типа, скажем, Collection<User>
, и заставить компилятор вывести T
как User
контекстуально из желаемого типа возврата:
const model2: Collection<User> = createCollection({ key: 'id' });
model2.add(new User()); // okay
Если вы хотите сохранить фиктивный аргумент _ModelClass
и ваше намерение состоит в том, чтобы передать класс конструктор вместо класса экземпляр , тогда createCollection()
имеет неправильную подпись. Объявления классов, такие как ваш пример User
, представляют как именованный тип интерфейса , соответствующий типу экземпляров класса, так и именованное значение , относящееся к конструктору класса. И они разные, несмотря на одинаковое название. Значение конструктора с именем User
не относится к типу User
.
Тип с именем User
вроде как {id: number; [key: string]: any;}
; объект со свойством numeri c id
и множеством других возможных свойств, в то время как тип значения User
(то есть typeof User
,) вроде как new () => User
; a newable объект, который создает значение типа User
при вызове с new
.
Если вы хотите узнать больше о различиях между типами и значениями и как одно и то же имя может относиться к типу и значению в зависимости от контекста, вы можете посмотреть другой мой длинный монолог на эту тему .
В любом случае, если ваше намерение чтобы принимать конструкторы, а не экземпляры, вы должны аннотировать тип параметра _ModelClass
не как T
, а как что-то вроде new (...args: any) => T
, что означает «новый конструктор, который принимает некоторое количество аргументов, которые нам не нужны, и производит значение типа T
":
function createCollection<T extends Model>(
_ModelClass: new (...args: any) => T,
options: CollectionOptions
): Collection<T> {
const collection: Collection<T> = new Collection(options);
return collection;
}
Тогда ваша функция примет конструктор User
:
const model = createCollection(User, { key: 'id' });
model.add(new User()); // okay
Но пожаловаться на экземпляр User
:
const modelBad = createCollection(new User(), { key: 'id' }); // error!
// Type 'User' is not assignable to type 'new (...args: any) => Model`
Если по какой-то причине вы хотите, чтобы компилятор принимал либо экземпляр , либо конструктор как _ModelClass
, вы можете аннотировать этот параметр как объединение инст Типы и конструктор:
function createCollection<T extends Model>(
_ModelClass: (new (...args: any) => T) | T,
options: CollectionOptions
): Collection<T> {
const collection: Collection<T> = new Collection(options);
return collection;
}
И тогда он будет работать в обоих направлениях:
const modelCtor = createCollection(User, { key: 'id' });
modelCtor.add(new User()); // okay
const modelInstance = createCollection(new User(), { key: 'id' }); // error!
modelInstance.add(new User()); // okay
Хорошо, надеюсь, это поможет; удачи!
Детская площадка ссылка на код