#typescript
Вопрос:
Мне нужно определить схему «операций», которые будут использоваться в моем приложении. Эта схема должна быть расширяемой для других групп «операций». Он должен содержать словарь настроек для каждого ключевого слова.
В какой-то момент «общий вызывающий абонент» должен получить тип «операции» плюс ключевое слово и создать его кэш для последующих вызовов.
Мне также нужен этот «универсальный вызывающий», чтобы убедиться, что запрошенное ключевое слово определено в операции во время компиляции, поэтому оно показывает ошибки разработчикам в коде VS.
Ниже приведено решение, которое очень близко к тому, что мне нужно:
// Operation interface
interface Operation {
url: string
parameters: Record<string,string>
}
// Operations schema
class Operations {}
class BaseOperations extends Operations {
one: {
url: '/one',
parameters: {p: '1'}
}
two: {
url: '/two',
parameters: {}
}
}
// Generic caller (which caches the calls)
function runOperation<T extends Operations>(type: {new(): T;}, keyword: keyof T) {
let operation = new type();
//cache_and_run(operation[keyword]);
}
// Main
function main() {
runOperation(BaseOperations, 'one');
runOperation(BaseOperations, 'two');
runOperation(BaseOperations, 'three'); // VS Code error, as expected
}
Единственная проблема здесь заключается в том, что параметры, определенные в Operations
, не привязаны к Operation
интерфейсу. Это незначительная проблема, однако я хотел бы иметь возможность убедиться, что оба конца (определения операций и их использование) проверяются во время компиляции.
После некоторых исследований я нашел параметр «подпись индекса», который позволяет применять возвращаемый тип:
class BaseOperations extends Operations {
[x:string]: Operation
one: {
url: '/one',
parameters: {p: '1'}
}
two: { // 'uarl' is not assignable
uarl: '/two'
}
}
Однако этот подход отключил 'one'|'two'
проверку runOperation
, поскольку любая строка теперь является допустимой keyof BaseOperations
.
Есть какие-нибудь предложения?
Ответ №1:
Вместо использования подписи индекса вы можете добавить implements
предложение в объявление класса, чтобы попросить компилятор проверить, BaseOperations
можно ли назначить реализованный тип. В этом случае я бы использовал самореферентный тип, такой как Record<keyof BaseOperations, Operation>
:
class BaseOperations extends Operations
implements Record<keyof BaseOperations, Operation> {
one!: {
url: '/one',
parameters: { p: '1' }
}
two!: { // error! 'uarl' is not assignable
uarl: '/two'
}
}
В приведенном выше есть ошибка при two
объявлении. Если вы исправите это, то класс будет компилироваться без ошибок. Между тем компилятор все еще точно знает, какие ключи существуют в a BaseOperations
, потому что он не расширил свой набор ключей до string
:
runOperation(BaseOperations, 'one'); // okay
runOperation(BaseOperations, 'two'); // okay
runOperation(BaseOperations, 'three'); // error!
Комментарии:
1. В точку! Я пытался
implements Record<string,Operation>
и застрял на той же проблеме, что и раньше. Ссылка на класс из интерфейса, который он реализует, звучит одновременно странно и красиво. Ваше решение работает идеально, большое вам спасибо за потраченное время.