0
export interface Action{
    type: string;
}

    export interface LoadTodos {
      type: "LOAD_TODOS_ACTION"
    }

    export interface AddTodo {
      type: "ADD_TODO_ACTION",
      todo: Todo
    }

export type KnownAction = LoadTodos | LoadTodosSuccess | AddTodo;

function isSomeType<T extends KnownAction>(x: any): x is T {
    return x && typeof (x.type) === "LOAD_TODOS_ACTION";
}


let knownAction: actions.KnownAction = { type: "LOAD_TODOS_ACTION" };


if (isSomeType(knownAction)) {
    let loadTodosAction = knownAction; // load Todos type
}

I'm expecting the behaviour above. I want to do this in a generic way so I don't want to repeat if, else statement in isSomeType function.Basically I want to convert string literal type into appropriate type based on 'type' property. My attempt was to do this:

function isSomeType<T extends Action>(x: any): x is T {
    type p1 = T['type'];
    return x && typeof (x.type) === p1;
}

But the message says: 'p1' only refers to a type but is used as a variable here.

Is there a way to do this?

2 Answers 2

1

Surprisingly the answer is: do nothing.

function isSomeType<T extends KnownAction>(x: T):T {
    return x;
}

Basically typescript will interfere type automatically whenever function with the above signature is called.

EDIT:

 let knownAction: actions.KnownAction = { type: "LOAD_TODOS_ACTION" }; // KnownAction type
 let knownAction2 = isSomeType(knownAction) // LoadTodosAction type
Sign up to request clarification or add additional context in comments.

5 Comments

isn't this always trivially true?
I wouldn't expect this behavior. I have .NET background and if you pass an object of type KnownAction you'll get KnownAction in return. Here you get an exact subtype.
So here's my confusion. Originally you were writing something that looked like a user defined type guard. I didn't realize that this was no longer a type guard at first. What you have here genuinely does nothing.
I am trying to convert string literal type into appropriate type without specyfiyng it explicitly. See edit.
this feels equivalent to let knownAction2 = knownAction
0

Not a definitive answer, but I don't believe you can.

FWIW the recommended approach would probably be a switch on the discriminating member.

As for your code, you may be losing track of where type information is lost. The typeof operator will not be meaningful here. typeof is a runtime operation and even if you write type: "LOAD_TODOS_ACTION" in your code at runtime typeof x.type will return string which I expect is not helpful to you. A string literal type means that the only acceptable assignable value is that string, not that a new type is introduced.

So in order to achieve what you're looking for we'd need to be able to get an interface member's compile time type information. As far as I know, there is no way to do this. When there is one, this is possible, but until then I don't believe it is.

1 Comment

It's not possible to do it this way because type aliases are not available in the runtime.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.