Ожидаемые 3 типа аргументов, но получил 1, но он должен выводить 2 типа - PullRequest
2 голосов
/ 19 апреля 2019

Мне интересно, как правильно вывести 2-й и 3-й шаблон моей функции

предположим, простой интерфейс

interface ISome {
    a: string;
    b?: {
        c: string;
    };
}

следовать работам

function pathBuilder<
    K1 extends keyof ISome,
    K2 extends keyof NonNullable<ISome[K1]>>(p: K1, p2?: K2) {
    let res = String(p);
    if (p2) { res += "." + p2; }
    return res;
}

const pathTest = pathBuilder("b", "c"); // ---> "b.c" and intellisense works on parameters

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

так, следующее не работает

function pathBuilder<
    T,
    K1 extends keyof T,
    K2 extends keyof NonNullable<T[K1]>>(p: K1, p2?: K2) {
    let res = String(p);
    if (p2) { res += "." + p2; }
    return res;
}

const pathTest = pathBuilder<ISome>("b", "c"); // ERROR: Expected 3 type arguments, but got 1.ts(2558)

кажется, что 2-й и 3-й аргументы шаблона функции не выводят из первого, но это должно произойти, потому что в первом случае, когда я непосредственно указал тип T = ISome, это сработало.

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

EDIT

На самом деле я нашел этот способ, но мне нужно дополнительное кодирование, которого я бы по возможности избегал

function pathBuilder<T>() {
    return <
        K1 extends keyof T,
        K2 extends keyof NonNullable<T[K1]>>(p: K1, p2?: K2) => {
        let res = String(p);
        if (p2) { res += "." + p2; }
        return res;
    };
}

const pathTest = pathBuilder<ISome>()("b", "c");

1 Ответ

3 голосов
/ 19 апреля 2019

Начиная с TS3.4 нет логического вывода параметра частичного типа .Либо вы позволяете компилятору выводить все параметры типа, либо вы указываете все параметры типа.(Ну, есть параметры типа по умолчанию , но это не дает вам того, что вы хотите: вы хотите выводить параметров типа, которые вы опускаете, а не присваивать значения по умолчанию типа им).На адрес было несколько предложений, но пока ни одно не получило полного одобрения .

Поэтому пока есть только обходные пути.Два, о которых я могу думать, это использовать фиктивный параметр функции или использовать curry .

Версия фиктивного параметра:

function pathBuilderDummy<
    T,
    K1 extends keyof T,
    K2 extends keyof NonNullable<T[K1]>>(dummy: T, p: K1, p2?: K2) {
    let res = String(p);
    if (p2) { res += "." + p2; }
    return res;
}

const pathDummyTest = pathBuilderDummy(null! as ISome, "b", "c");

Здесь мы делаем то, чтоВы сказали, что не хотите делать ... передать параметр типа T.Но поскольку это просто фиктивный параметр, который не используется во время выполнения, имеет значение только то, что думает система типов.Фактический тип значения, которое вы передаете, не имеет значения.Таким образом, вы можете просто передать его null и использовать утверждение типа , чтобы выбрать T.

Решение с функцией карри:

const pathBuilderCurry =
    <T>() => <
        K1 extends keyof T,
        K2 extends keyof NonNullable<T[K1]>>(p: K1, p2?: K2) => {
        let res = String(p);
        if (p2) { res += "." + p2; }
        return res;
    }

const pathCurryTest = pathBuilderCurry<ISome>()("b", "c")

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

Ни одно из решений не является идеальным, но они являются лучшим, что мы можем сделать на данный момент.Надеюсь, это поможет;удачи!

...