Kozack
@Kozack
Thinking about a11y

Как можно ограничить интерфейс только методами?

У меня в приложении есть система для IPC (Inter Process Communications). Все виды комуникации разделены по разным сервисам. У меня описаны интерфейсы для каждого сервиса, есть словарь всех сервисов и функция которая по названию, возвращает конкретный сервис.

Упродённо это выглядит примерно так:
// Интерфейс для одного сервиса
interface Service_FOO {
    methodFoo: () => void
}

// Интерфейс для другого сервиса
interface Service_BAR {
    methodBar: () => void
    propertyBar: boolean // <-- Должно привести к ошибке
}


// Словарь всех сервисов и их названий
interface ServiceMap {
    'Service_FOO': Service_FOO
    'Service_BAR': Service_BAR // <-- Или привести к ошибке здесь
}


// Функцыя которая создаёт и возвращает сервис
function createClient<T extends keyof ServiceMap>(name: T) : ServiceMap[T] {
    return {} as ServiceMap[T]
}


// Пользовательский код
const serviceFoo = createClient('Service_FOO')
serviceFoo.methodFoo()


const serviceBar = createClient('Service_BAR')
serviceBar.propertyBar // Приведёт к ошибке в рантайме

Playground

Проблема в том, что из-за особенностей реализации createClient все сервисы могут иметь только методы. Отсюда мой вопрос:
Как можно ограничить сам интерфейс чтобы он содержал только методы? Или как ограничить словарь ServiceMap чтобы он мог содержать только интерфейсы в которых только методы?

Я попытался сделать вот так:
interface Service_FOO extends Record<string, () => void> {
    methodFoo: () => void
}


interface Service_BAR extends Record<string, () => void>  {
    methodBar: () => void
    
    // @ts-expect-error
    propertyBar: boolean
}


// Class 'FOO' incorrectly implements interface 'Service_FOO'.
// Index signature is missing in type 'FOO'.
class FOO implements Service_FOO {
    methodFoo() {}
}

Playground

И я действительно получаю ошибку типов в интерфейсе Service_BAR, но в месте с этим я начал получать ошибку в классе FOO:
Class 'FOO' incorrectly implements interface 'Service_FOO'.
  Index signature is missing in type 'FOO'.
  • Вопрос задан
  • 54 просмотра
Решения вопроса 1
Aetae
@Aetae
Тлен
Тут понадобится немного магии:
type ClearIndex<T> = {
  [ P in keyof T as string extends P ? never : P ] : T[P]
};
class FOO implements ClearIndex<Service_FOO > {
    methodFoo() {}
}


Суть проблемы в том что Record<string, () => void> имеет "index signature", т.е. заявляет, что обращение по любому ключу stringвернёт () => void.
Service_FOO расширяет этот тип(а не сужает), потому Service_FOO имеет ту же index signature и уточнённый конкретный метод.
Класс же в ts не может иметь index signature, т.к. является строгой и конкретной структурой.
Ответ написан
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы