Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Typescript Generic Type Assertion

So Here is a summary of my observation working with typescript.

Here is some code:

type someTypeEnum = '1';
type someOtherTypeEnum = '2' | '3';
type combinedTypeEnum = someTypeEnum | someOtherTypeEnum;

Here is the first case :-

function typeAssertion<T extends combinedTypeEnum>(args: T): args is someTypeEnum {
    // The error i get
    // A type predicate's type must be assignable to its parameter's type.
    //  Type '"1"' is not assignable to type 'T'.
    return undefined;
}

I cannot understand why this thing fails coz we have already limited our arguments to combinedTypeEnum, in case we do

typeAssertion('4')

We already get an error stating '4' is not a valid argument so why is it that args is someTypeEnum is considered an in-valid predicate.

Here is the second case :-

function typeAssertion(args: combinedTypeEnum): args is someTypeEnum {
    return undefined;
}

This seems to work fine but in case we do this :-

function someFunction<T extends combinedTypeEnum>(args: T): T {
    if (typeAssertion(args)) {
        // args here is  'T & "1"' 
        args
    }
    return args
};

why is it that we have T & "1" and not only "1", we specifically asserted that it is someTypeEnum.

I was really curious as to why such decisions were made. It would be really helpful to see how things break in case things were done in a different manner.

like image 432
Amol Gupta Avatar asked Oct 27 '25 09:10

Amol Gupta


1 Answers

extends doesn't make much sense when you have string literals. To make the explanation easier let me use other types. Consider these three classes:

class Animal {}

class Dog extends Animal {}

class Cat extends Animal {}

when we use generics the actual type is set by the caller:

function foo<T extends Animal>(arg: T) {}

foo(new Dog()); //T is Dog, equivalent to foo(arg: Dog) {}
foo(new Cat()); //T is Cat, equivalent to foo(arg: Cat) {}

Now you may already see where we are going. Let's use a type predicate:

function foo<T extends Animal>(arg: T): arg is Cat {}

When we call foo(new Dog()) the last example becomes equivalent to this:

function foo(arg: Dog): arg is Cat {}

And of course it doesn't work or make sense.

As for your second example: The type of the variable doesn't change. The point is that by asserting a specific type the compiler allows you to do whatever can be done with this type.

like image 168
a better oliver Avatar answered Oct 28 '25 23:10

a better oliver



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!