¿Cómo crear el equivalente de las clases de variantes de enum de Rust, o las clases selladas de Kotlin en TypeScript?
Me gustaría crear un tipo variante en TypeScript, para respuestas HTTP, que pueda tener varios estados. En Rust, podría crear este tipo como:
enum Response<T> {
Empty,
Loading,
Failure(String),
Success(data: T),
}
Lo mismo se podría hacer en Kotlin usando clases selladas:
sealed class Response<out: T> {
object Empty: Response<Nothing>()
object Loading: Response<Nothing>()
class Failure(val err: string): Response<Nothing>()
class Success(val data: T): Response<T>
}
¿Hay alguna manera de crear este tipo de variante en TypeScript?
Respuesta #1
Gracias a @gabriele-petrioli por la idea de usar una unión discriminada.
type EmptyState = {
kind: "empty";
};
type LoadingState = {
kind: "loading"
};
type FailedState = {
kind: "failed"
msg: string;
};
type SuccessState<T> = {
kind: "success"
data: T;
};
type RequestState<T> =
| EmptyState
| LoadingState
| FailedState
| SuccessState<T>;
function parse<T>(res: RequestState<T>) {
switch (res.kind) {
case "failed":
res.msg;
break;
case "success":
res.data;
}
}
Respuesta #2
Aquí hay un tipo genérico que construye la unión automáticamente.
También he incluido una función utilidad intoEnum para facilitar su uso.
type PartialRustEnum<K extends string, V> = {
kind: K
} & {
[P in K]: V
}
export type RustEnum<T> = {
[K in keyof T & string]: PartialRustEnum<K, T[K]>;
}[keyof T & string]
export function intoEnum<K extends string, V>(kind: K, value: V): PartialRustEnum<K, V> {
return { kind, [kind]: value } as any
}
uso :
type FooOrBar = RustEnum<{
foo: number
bar: string
}>
let a: FooOrBar = intoEnum('foo',32)
if(a.kind === 'foo'){
// inferred as foo
console.log(a.foo)
}else{
// inferred as bar
console.log(a.bar)
}
