Kir
Kir

Reputation: 3072

How would I structure types to represent this?

Let's say I have an interface

interface FooBranchState {
  a: string;
  b: FooDiscriminator;
}

type FooDiscriminator = (value: any) => FooState;

I want to define FooState to be a type or interface representing the following:

and also

I've currently got


export type FooState = FooEndState | FooBranchState

export type FooDiscriminator = (value: any) => FooState;

export type FooEndState = null;

export type FooBranchState = SingleFooBranchState | (SingleFooBranchState[])

export interface SingleFooBranchStateImpl {
  foo: any;
  next: FooDiscriminator;
}

export type SingleFooBranchStateFunction = () => SingleFooBranchStateImpl 
export type SingleFooBranchState = SingleFooBranchStateImpl | SingleFooBranchStateFunction;


function test(value: FooState) {
  console.log(value);
}

function getSingle(): FooState {
  return { 
    foo: 1,
    next: () => null
  };
}

function getNull(): FooState {
  return null;
}


function getArray(): FooState {
  return [
    getSingle(),
    getSingle()
  ];
}

function getFunctionSingle(): FooState {
  return () => ({
    foo: 2,
    next: () => null
  });
}

function getFunctionArray(): FooState {
  return () => ([{
    foo: 3,
    next: () => null
  }]);
}

function getPromise(): FooState {
  return Promise.resolve({
    foo: 4,
    next: () => null
  });
}

But assigning a FooState[] to FooState requires a cast to FooState, otherwise I get the below error. This makes me think there's something incorrect in my definition.

The error is:

Type 'FooBranchState[]' is not assignable to type 'FooBranchState'. Type 'FooBranchState[]' is not assignable to type 'SingleFooBranchState[]'. Type 'FooBranchState' is not assignable to type 'SingleFooBranchState'. Type 'SingleFooBranchState[]' is not assignable to type 'SingleFooBranchState'. Type 'SingleFooBranchState[]' is not assignable to type 'SingleFooBranchStateFunction'. Type 'SingleFooBranchState[]' provides no match for the signature '(): SingleFooBranchStateImpl'.

edit: corrected some typos in the original question (I was simplifying my actual code to a condensed example and made a couple errors).

As requested, here's an example: https://stackblitz.com/edit/typescript-xyag2s

edit2: Based on some of the comments on this, I have thought about this a little harder and came up with a more distilled example: http://www.typescriptlang.org/play/?ssl=1&ssc=1&pln=18&pc=1#code/C4TwDgpgBAogtmUUC8UB2BXANlg3AWAChRIoANAJhSgGdgAnASzQHMDjxozrKoAfchQDaAXX6wEodkSIAzDGgDGwRgHs0UFhGABlZiywQAFAEoAXOSgBvKAHpbUAO6r6AaxpEoUetoz0NAOQAFowB7AC+MoTySirqmtoActhYphbcNvZOLu6e3r7+6CkRUTHKahpawACC9PQAhiBpljZ2DhB1LgA0UABGEIr1GDRcUIMa-UU4Pb0YwIJj9WgB85OYOHk+wH4aQl77B4deWdzjfdBLUPV1jVCqsoIzc1caZKLZ2AAmV1g0qmPqYD1Zh5LxVPSsQymI6HLJ7dZYHoBOhMVgBKAiPIiEqEOQKcrxKq1BogCjNDJtbJuDyELxbHZQIQrCB0ALYojhIA

type Empty = null;
type X2 = string;
type X = X2 | X2[] | Empty;


function getSingle(): X { // works
  return 'hi';
}

function getNull(): X { // works
  return null;
}

function getArray(): X {  // error, because X can be null, but X2 can't be null
  return [                // X can be an array of X2, but an X[] would also contain
    getSingle()           // [ null, 'string' ]
  ];
}

function getArray2(): X { // works
  return ['test'];
}

In the simple example, it almost makes sense to declare getSingle as an X2, but for my actual needs, I need to be able to return multiple variants from the same function.

Upvotes: 0

Views: 56

Answers (1)

alexortizl
alexortizl

Reputation: 2720

It could be something like:

export type Foo = any;

export type FooState = null | Foo | Array<Foo> | FooDistriminator | FooDistriminatorPromise;

export type FooDistriminator = (value: any) => null | Foo | Array<Foo>;

export type FooDistriminatorPromise = (value: any) => Promise<null | Foo | Array<Foo>>;

Edit: In your stackblitz problem seems to be with your function typings for passed value and return value. I got it working like:

export type FooState = FooEndState | FooBranchState

export type FooDiscriminator = (value: any) => FooState;

export type FooEndState = null;

export type FooBranchState = SingleFooBranchState | Array<SingleFooBranchState> | Promise<SingleFooBranchStateImpl>

export interface SingleFooBranchStateImpl {
    foo: any;
    next: FooDiscriminator;
}

export type SingleFooBranchStateFunction = () => SingleFooBranchStateImpl
export type SingleFooBranchState = SingleFooBranchStateImpl | SingleFooBranchStateFunction;


function test(value: FooState) {
    console.log(value);
}

function getSingle(): SingleFooBranchStateImpl {
    return {
        foo: 1,
        next: () => null
    };
}

function getNull(): FooState {
    return null;
}


function getArray(): Array<SingleFooBranchState> {
    return [
        getSingle(),
        getSingle()
    ];
}

function getFunctionSingle(): FooState {
    return () => ({
        foo: 2,
        next: () => null
    });
}


function getPromise(): Promise<SingleFooBranchStateImpl> {
    return Promise.resolve({
        foo: 4,
        next: () => null
    });
}

test(null);
test(getSingle());
test(getNull());
test(getArray());
test(getFunctionSingle());
test(getPromise());

Upvotes: 1

Related Questions