jon
jon

Reputation: 1818

branding with type predicates in intersections

I have a small problem that I've been unable to solve after several hours. I don't know if a sharper mind could offer me a solution?

But unfortunately, i can't seem to find a solution to allow this approach. Maybe a lacking from typescript !?

Everything work as i want, but this thing drive me crazy :) ! If anyone has any ideas ho to remove this last error in my code ? see: 🔴

I've simplified the example to the minimum

thanks a lot

type RemoveSuper<A extends number[]> = { // remove number constructor in tuple [number,1,2] => [1,2]
    [K in keyof A]: number extends A[K] ? never : A[K]
}

type EntityWith<N extends number[] > = { //entity with numbers (branding) 
    has< T extends N >( componentType: T ):boolean
}

type ExtractGenericInIntersection<T> = (
    T extends Entity<infer C> ? ( c:C[number]) => void : never
) extends ( r: infer R2 ) => void ? [R2[]] : never

type Entities<T extends Entity> = ExtractGenericInIntersection<T> extends [infer R extends number[]]
    ? EntityWith<R> | Entity<R>
    : never

export interface Entity<C extends number[] = number[]> {
    has: <T extends RemoveSuper<C>[number]>( componentType: T ) => this is Entity<[T]>;
}
type A = Entity<[1]>
type B = Entity<[1, 2]>

declare const entities: Entities<A|B>;

//@ts-expect-error
entities.has( 2 );

entities.has( 1 );//🔴

tiny version

short version

long version

Upvotes: 1

Views: 51

Answers (1)

jon
jon

Reputation: 1818

The problem was related to a limitation of type that do not handle this.

Interfaces seem to support this, which allows branding works.

By replacing type with interface the branding work.

type EntityWith<
    C extends Component,
    R extends ComponentType = never,
    E extends Entity = Entity,
> = E & {
    has<T extends R>(componentType: T): boolean
}

by

interface EntityWith<
    C extends Component,
    R extends ComponentType = never,
> extends Entity {
    has< T extends R>( componentType: T ): this is Entity<InstanceType<T>, []>
}

Now it reflet correcly the method in class

abstract class Entity {
    declare has: <T extends RemoveSuper<M>>(componentType: T) => this is Entity<InstanceType<T>, []>
}

Upvotes: 0

Related Questions