Можно ли комбинировать элементы нескольких типов в аннотации TypeScript?

Кажется, что то, что я пытаюсь сделать, невозможно, но я действительно надеюсь, что это так.

По сути, у меня есть два интерфейса, и я хочу аннотировать один параметр функции как комбинацию обоих из них.

interface ClientRequest {
    userId:     number
    sessionKey: string
}

interface Coords {
    lat:  number
    long: number
}

И затем, в функции, я хочу сделать что-то вроде этого:

function(data: ClientRequest&Coords) { ... }

Итак, чтобы мой объект "data" мог содержать все члены обоих типов.

Я видел что-то, упоминаемое в (spec preview) [https://github.com/Microsoft/TypeScript/issues/805] в разделе "Объединение типов", но похоже, что это еще не сделал этого.

Если это невозможно, мое решение может выглядеть так:

interface ClientRequest<T> {
    userId:     number
    sessionKey: string
    data?:       T
}

function(data: ClientRequest<Coords>) { ... }

Что будет работать в этом случае, хотя это не так динамично, как хотелось бы. Мне бы очень хотелось объединить несколько (2+) типов в самой аннотации:

function(data: TypeA&TypeB&TypeC) { ... }

Я бы предположил, что обычное решение состоит в том, чтобы определить тип, который расширяет эти типы, хотя это кажется менее гибким. Если я хочу добавить тип, мне придется либо (а) вернуться к объявлению и переписать его, либо (б) создать совершенно новый интерфейс. Не уверен, что я согласен с дополнительными накладными расходами.

Любые эксперты TypeScript должны указать мне в правильном направлении?

Ответ 1

Конкретный ответ на ваш вопрос: нет, нет единой встроенной аннотации для обозначения комбинированных или расширенных типов.

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

interface IClientRequestAndCoords extends IClientRequest, ICoords {} 

function(data: IClientRequestAndCoords) 

Ответ 2

Ответ интерфейса - разумно грациозный метод объединения двух структур, но вы говорите, что хотите знать, можно ли комбинировать этот тип как часть аннотации.

Заметка о интерфейсах

Я предоставил несколько описаний нескольких функций, связанных с вашим вопросом, но сначала я бы сказал, что если вы отложите решение интерфейса, потому что считаете, что вам нужно создать интерфейс ICoords (как в вашем вопрос он больше похож на класс) - легко отдохнуть - потому что интерфейс также может расширить класс:

// Interface extending an interface and a class
interface IClientRequestAndCoords extends IClientRequest, Coords {} 

Интерфейс будет даже объединять свойства, если они имеют одинаковое имя и тип. (Например, если оба объявили свойство x: string.

Ниже приведены примечания к другим функциям аннотации, на которые вы ссылаетесь.

Типы соединений

Спецификация, которую вы, возможно, читали, представляет собой тип объединения, который выглядит следующим образом:

var x: IClientRequest | Coords;

Но это только гарантирует, что x либо тот, либо другой, а не комбинация из двух. Насколько я знаю, ваш синтаксис объединенного типа IClientRequest & Coords не входит в дорожную карту.

function go(data: IClientRequest | Coords) {
    var a = data[0]; // IClientRequest
    var b = data[1]; // Coords
}

// Allowed (even though it doesn't supply Coords data
go(myClientRequest);

// Allowed (even though it doesn't supply IClientRequest data
go (myCoords);

Это также не является частью текущей версии, но подходит позже.

Типы кортежей

Другая возможная часть спецификации, которую вы, возможно, видели, - это типы кортежей:

var x: [IClientRequest, Coords];

Но это изменило бы форму данных на то, что структура является похожим на массив, где элемент 0 является IClientRequest, а элемент 1 - Coords.

function go(data: [IClientRequest, Coords]) {
    var a = data[0]; // ClientRequest
    var b = data[1]; // Coords
}

go([myClientRequest, myCoords]);

Uber-аннотаций

И, наконец, если вы действительно не хотите создавать объединенный интерфейс, вы можете просто использовать аннотацию uber:

function go(data: { userId:number; sessionKey: string; x: number; y: number; } ) {

}

Ответ 3

Это очень возможно, если вы используете ES6 Object.assign

interface ClientRequest {
    userId:     number
    sessionKey: string
}

interface Coords {
    lat:  number
    long: number
}

type Combined = ClientRequest & Coords;

Затем вы можете определить свои данные следующим образом:

let myData = Object.assign({},
    <ClientRequest> {
        userId: 10,
        sessionKey: "gjk23h872ty3h82g2ghfp928ge"
    },
    <Coords> {
        lat: -23,
        long: 52
    }
);

Наконец, вызовите функцию:

function myFunc(data: Combined) { ... }
myFunc(myData);

Посмотрите на другой вопрос, чтобы еще больше сделать это:

Как я могу объединить свойства двух объектов JavaScript динамически?

Ответ 4

Я видел что-то, упоминаемое в (spec preview) [https://github.com/Microsoft/TypeScript/issues/805] в разделе "Объединение типов", но похоже еще не сделал этого.

Думаю, вас больше интересуют типы intersection (не union). Разница в том, что переданный объект должен поддерживать все свойства, а не один из них.

Проблема Github: https://github.com/Microsoft/TypeScript/issues/1256#issuecomment-64533287