Получить ключи типа на основе типа одного из его значений - PullRequest
0 голосов
/ 03 апреля 2020

У меня есть определение глобального типа, которое представляет собой объект с ключами (то, что я называю prop), который пользователь должен передать в качестве параметра myFunction(), и значения, которые используются только моим кодом для проверки типов .

Значения могут иметь только 3 «возможных типа» (содержится в Possible). Каждый возможный тип имеет свое определение типа.

Я бы хотел, чтобы myFunction() принимал только в качестве параметра props, чье значение test свойство "possible1".

generi c type OnlyPossible1Props<T> показывает способ, которым я бы это реализовал, но он не работает.

Сначала отображается сообщение об ошибке:

Тип 'prop' не может использоваться для тип индекса 'T'. (2536)

И, кроме того, он возвращает string | number вместо "abc" | "def" в myFunction().

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

Для простоты отладки, вот ссылка на TypeScript Playground .

type Possible = "possible1" | "possible2" | "possible3";

type ValueBase<T extends Possible, U, V> = {
    test: T,
    other1: U,
    other2: V
};

type Value1<T> = ValueBase<"possible1", T, T>;
type Value2<T> = ValueBase<"possible2", T, [T, T]>;
type Value3<T> = ValueBase<"possible3", T, T[]>;

type Definition = {
    [prop: string]: Value1<any> | Value2<any> | Value3<any>
};

interface Example extends Definition {
    "abc": {
        test: "possible1",
        other1: string
        other2: string
    },
    "def": {
        test: "possible1",
        other1: string
        other2: string
    },
    "ghi": {
        test: "possible2",
        other1: string
        other2: [string, string]
    },
    "jkl": {
        test: "possible3",
        other1: string
        other2: string[]
    }
}

// type OnlyPossible1Props<T> = T extends object ?
//     T[keyof T] extends infer A ?
//     A extends { test: "possible1" }
//     ? A : never : never : never;

type OnlyPossible1Props<T> = T extends object ?
    keyof T extends infer prop ?
    T[prop] extends infer A ?
    A extends { test: "possible1" }
    ? prop : never : never : never : never;

function myFunction<Prop extends OnlyPossible1Props<Example>>(prop: Prop) { }

1 Ответ

1 голос
/ 04 апреля 2020

Следующее работает, но только если вы удаляете extends Definition из Example:

type OnlyPossible1Props<T> = {
    [K in
        { [J in keyof T]: T[J] extends { test: "possible1" } ? J : never }[keyof T]
    ]?: T[K]
};

Это приводит к:

type ExampleOnlyPossible1Props = OnlyPossible1Props<Example>;

// equivalent to:

type ExampleOnlyPossible1Props = {
    abc?: {
        test: "possible1";
        other1: string;
        other2: string;
    };
    def?: {
        test: "possible1";
        other1: string;
        other2: string;
    };
}

(Вы можете пропустить необязательный модификатор ?).

Это дает нам:

myFunction({ "abc": { test: "possible1", other1: "foo", other2: "bar" } }) // OK
myFunction({ "def": { test: "possible2", other1: ["a", "b"], other2: "bcd" } }) // Error

Вы можете видеть, что на этой Игровой площадке TypeScript .

Причина, по которой это делает не работает, если Example extends Definition означает, что подпись индекса на Definition означает, что keyof Example соответствует string | number, а не фактическим ключам Example. Если extends Definition удалено, мы получим keyof Example как "abc" | "def" | "ghi" | "jkl".

Если вы хотите, чтобы myFunction принял один из possible1 реквизитов от Example, вы вместо этого можно использовать:

type OnlyPossible1Props<T> = {
    [J in keyof T]: T[J] extends { test: "possible1" } ? T[J] : never
}[keyof T];

Это дает вам тип объединения, который вы можете видеть на этой TypeScript Playground :

myFunction({ test: "possible1", other1: "foo", other2: "bar" }) // OK
myFunction({ test: "possible2", other1: ["a", "b"], other2: "bcd" }) // Error
...