Условный тип, основанный на значении переменной в классе (TypeScript) - PullRequest
2 голосов
/ 07 мая 2019

Я создаю универсальный класс с именем Loadable<T> с двумя полями.Один из них называется state и содержит текущее состояние загружаемого ресурса.Второй называется data и содержит значение загружаемого ресурса.

state определяется как "loading" | "loaded" | "error", и я хотел бы ввести data для изменения в зависимости от текущего значения state.

Например, если state равно "loading", тогда data должно быть null.Я не могу понять синтаксис, чтобы сделать это.

Я создал type с именем LoadableState<T, U>, который определен как:

    type LoadableState<T, U> =
        T extends "loaded" ? U :
        T extends "loading" ? null :
        T extends "error" ? string :
        never;

Где передано T должно бытьвведите "loaded" | "loading" | "error".

Эта часть работает, но попытка определить data из этого не дает.

export class Loadable<T> {
    public state: "loaded" | "loading" | "error" = "loading";

    public data: LoadableState<state, T>;
    //                         ^^^^^ Cannot find name 'state`
}

TypeScript выдает следующую ошибку: Cannot find name 'state'


Другие вещи, которые я пробовал:

public data: LoadableState<this.state, T>;
// Duplicate identified 'state'

public data: LoadableState<typeof this.state, T>;
// Cannot find name 'this'

public data: LoadableState<typeof state, T>;
// Cannot find name 'state'

public data: state extends "loaded" ? T :
    state extends "loading" ? null :
    state extends "error" ? string :
    never;
// Cannot find name 'state'

Я не уверен, возможно ли это на самом деле.Если это не так, есть ли другое решение этой проблемы?

1 Ответ

7 голосов
/ 07 мая 2019

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

Вам лучше объединить оба эти свойства в один объект:

type LoadState<T> = {
    state: "loading",
} | {
    state: "loaded",
    data: T
} | {
    state: "error",
    data: string; // or maybe 'message'
}

export class Loadable<T> {
    state: LoadState<T> = { state: "loading" }
}

Хотя обертка класса здесь на самом деле не очень полезна, вы можете подумать о том, чтобы вообще не использовать этот класс.

При использовании этого типа Typescript принудительно проверяет state перед попыткой доступа к данным:

function handleLoadState<T>(loadState: LoadState<T>) {
   loadState.data // ERROR, can't access data, since it might not exist
   if(loadState.state === "loaded")
       loadState.data // okay
   }
}

Этот шаблон называется Дискриминационный Союз и довольно часто используется в Typescript.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...