TypeScript: условные универсальные типы в качестве возвращаемых значений функции не могут быть назначены

У меня проблема с присваиваемыми значениями в одной из моих общих функций:

interface BuildArguments<T extends string> {
    type: T;
}

type PromiseResult<T> =
    T extends 'standalone' ? Promise<void> :
    T extends 'all' ? Promise<void> :
    Promise<void[]>;    

const foo: PromiseResult<'standalone'> = Promise.resolve();
const bar: PromiseResult<'all'> = Promise.resolve();
const baz: PromiseResult<'foo'> = Promise.resolve([]);

bundle({ type: 'foo' });

function bundle<T extends string>(buildArguments: BuildArguments<T>): PromiseResult<T> {
    switch (buildArguments.type) {
        case 'standalone':
            return Promise.resolve(); // error here, not assignable to PromiseResult<T>
        case 'all':
            return Promise.resolve(); // error here, not assignable to PromiseResult<T>
        default:
            return Promise.all([ // error here, not assignable to PromiseResult<T>
                Promise.resolve(),
                Promise.resolve()
            ]);
    }
}

consts foo, bar и baz показывают, что условный тип работает нормально. вызов функции bundle({ type: 'foo' }) также правильно предоставляет тип Promise<void[]>, если вы используете игровую площадку ts и наводите на нее курсор. Почему это не работает для возвращаемых значений? Я также пробовал, если это вызвано тем, что TypeScript не может вывести T, добавив аргумент kind: T к функции, но без изменений. Утверждение Promise.resolve() на PromiseResult<T> работает нормально.

Зод: сила проверки и преобразования данных
Зод: сила проверки и преобразования данных
Сегодня я хочу познакомить вас с библиотекой Zod и раскрыть некоторые ее особенности, например, возможности валидации и трансформации данных, а также...
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Как заставить Remix работать с Mantine и Cloudflare Pages/Workers
Мне нравится библиотека Mantine Component , но заставить ее работать без проблем с Remix бывает непросто.
Угловой продивер
Угловой продивер
Оригинал этой статьи на турецком языке. ChatGPT используется только для перевода на английский язык.
TypeScript против JavaScript
TypeScript против JavaScript
TypeScript vs JavaScript - в чем различия и какой из них выбрать?
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Синхронизация localStorage в масштабах всего приложения с помощью пользовательского реактивного хука useLocalStorage
Не все нужно хранить на стороне сервера. Иногда все, что вам нужно, это постоянное хранилище на стороне клиента для хранения уникальных для клиента...
Что такое ленивая загрузка в Angular и как ее применять
Что такое ленивая загрузка в Angular и как ее применять
Ленивая загрузка - это техника, используемая в Angular для повышения производительности приложения путем загрузки модулей только тогда, когда они...
6
0
1 707
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Typescript, как правило, не позволит вам много делать с условными типами, пока они все еще имеют неразрешенные условные типы (как T в случае PromiseResult<T>)

Более того, в этом случае вы предполагаете, что сужение buildArguments.type сужает T. Это не. Сужение сужает значение, а не весь параметр типа. По-другому и быть не может, рассмотрим этот пример:

function foo<T extends string | number>(a: T, b: T) {
    if (typeof a === "string") {
        // should b be string? No
    }
}

foo<string | number>(1, "");

То, что мы сузили одно значение типа T, не имеет отношения к другим таким переменным.

Самое простое решение — использовать отдельную сигнатуру реализации, которая является более разрешительной, сохраняя при этом общедоступную сигнатуру с условными типами, которые лучше подходят для вызывающей стороны:

function bundle<T extends string>(buildArguments: BuildArguments<T>): PromiseResult<T>
function bundle(buildArguments: BuildArguments<string>): PromiseResult<'standalone' | 'all'> | PromiseResult<string> {
    switch (buildArguments.type) {
        case 'standalone':
            return Promise.resolve(); 
        case 'all':
            return Promise.resolve(); 
        default:
            return Promise.all([ 
                Promise.resolve(),
                Promise.resolve()
            ]);
    }
}

Другие вопросы по теме