44declare const __entityIdBrand : unique symbol ;
55
66/**
7- * Brand for wildcard relation IDs
7+ * Brand for EntityId type information
88 */
9- declare const __wildcardRelationBrand : unique symbol ;
9+ declare const __entityIdTypeBrand : unique symbol ;
1010
1111/**
1212 * Entity ID type for ECS architecture
@@ -15,9 +15,16 @@ declare const __wildcardRelationBrand: unique symbol;
1515 * - Entity IDs: 1024+
1616 * - Relation IDs: negative numbers encoding component and entity associations
1717 */
18- export type EntityId < T = void > = number & { readonly [ __entityIdBrand ] : T } ;
18+ export type EntityId < T = void , U = unknown > = number & {
19+ readonly [ __entityIdBrand ] : T ;
20+ readonly [ __entityIdTypeBrand ] : U ;
21+ } ;
1922
20- export type WildcardRelationId < T = void > = EntityId < T > & { readonly [ __wildcardRelationBrand ] : true } ;
23+ export type ComponentId < T = void > = EntityId < T , "component" > ;
24+ export type EntityRelationId < T = void > = EntityId < T , "entity-relation" > ;
25+ export type ComponentRelationId < T = void > = EntityId < T , "component-relation" > ;
26+ export type WildcardRelationId < T = void > = EntityId < T , "wildcard-relation" > ;
27+ export type RelationId < T = void > = EntityRelationId < T > | ComponentRelationId < T > | WildcardRelationId < T > ;
2128
2229/**
2330 * Constants for ID ranges
@@ -36,11 +43,11 @@ export const WILDCARD_TARGET_ID = 0;
3643 * Create a component ID
3744 * @param id Component identifier (1-1023)
3845 */
39- export function createComponentId < T = void > ( id : number ) : EntityId < T > {
46+ export function createComponentId < T = void > ( id : number ) : ComponentId < T > {
4047 if ( id < 1 || id > COMPONENT_ID_MAX ) {
4148 throw new Error ( `Component ID must be between 1 and ${ COMPONENT_ID_MAX } ` ) ;
4249 }
43- return id as EntityId < T > ;
50+ return id as ComponentId < T > ;
4451}
4552
4653/**
@@ -57,16 +64,24 @@ export function createEntityId(id: number): EntityId {
5764/**
5865 * Type for relation ID based on component and target types
5966 */
60- type RelationIdType < T , U > = U extends void ? EntityId < T > : T extends void ? EntityId < U > : EntityId < never > ;
67+ // type RelationIdType<T, U> = U extends void ? EntityId<T> : T extends void ? EntityId<U> : EntityId<never>;
68+ type RelationIdType < T , R > =
69+ R extends ComponentId < infer U >
70+ ? U extends void
71+ ? ComponentRelationId < T >
72+ : ComponentRelationId < T & U >
73+ : R extends EntityId < any >
74+ ? EntityRelationId < T >
75+ : never ;
6176
6277/**
6378 * Create a relation ID by associating a component with another ID (entity or component)
6479 * @param componentId The component ID (0-1023)
6580 * @param targetId The target ID (entity, component, or '*' for wildcard)
6681 */
67- export function relation < T > ( componentId : EntityId < T > , targetId : "*" ) : WildcardRelationId < T > ;
68- export function relation < T , U > ( componentId : EntityId < T > , targetId : EntityId < U > ) : RelationIdType < T , U > ;
69- export function relation < T > ( componentId : EntityId < T > , targetId : EntityId < any > | "*" ) : EntityId < any > {
82+ export function relation < T > ( componentId : ComponentId < T > , targetId : "*" ) : WildcardRelationId < T > ;
83+ export function relation < T , R extends EntityId < any > > ( componentId : ComponentId < T > , targetId : R ) : RelationIdType < T , R > ;
84+ export function relation < T > ( componentId : ComponentId < T > , targetId : EntityId < any > | "*" ) : EntityId < any > {
7085 if ( ! isComponentId ( componentId ) ) {
7186 throw new Error ( "First argument must be a valid component ID" ) ;
7287 }
@@ -88,26 +103,26 @@ export function relation<T>(componentId: EntityId<T>, targetId: EntityId<any> |
88103/**
89104 * Check if an ID is a component ID
90105 */
91- export function isComponentId ( id : EntityId < any > ) : boolean {
106+ export function isComponentId < T > ( id : EntityId < T > ) : id is ComponentId < T > {
92107 return id >= 1 && id <= COMPONENT_ID_MAX ;
93108}
94109
95110/**
96111 * Check if an ID is an entity ID
97112 */
98- export function isEntityId ( id : EntityId < any > ) : boolean {
113+ export function isEntityId < T > ( id : EntityId < T > ) : id is EntityId < T > {
99114 return id >= ENTITY_ID_START ;
100115}
101116
102117/**
103118 * Check if an ID is a relation ID
104119 */
105- export function isRelationId < T > ( id : EntityId < T > ) : boolean {
120+ export function isRelationId < T > ( id : EntityId < T > ) : id is RelationId < T > {
106121 return id < 0 ;
107122}
108123
109124/**
110- * Check if a entity ID is a wildcard relation id
125+ * Check if an ID is a wildcard relation id
111126 */
112127export function isWildcardRelationId < T > ( id : EntityId < T > ) : id is WildcardRelationId < T > {
113128 if ( ! isRelationId ( id ) ) {
@@ -123,8 +138,8 @@ export function isWildcardRelationId<T>(id: EntityId<T>): id is WildcardRelation
123138 * @param relationId The relation ID (must be negative)
124139 * @returns Object with componentId, targetId, and relation type
125140 */
126- export function decodeRelationId ( relationId : EntityId < any > ) : {
127- componentId : EntityId < any > ;
141+ export function decodeRelationId ( relationId : RelationId < any > ) : {
142+ componentId : ComponentId < any > ;
128143 targetId : EntityId < any > ;
129144 type : "entity" | "component" | "wildcard" ;
130145} {
@@ -133,8 +148,8 @@ export function decodeRelationId(relationId: EntityId<any>): {
133148 }
134149 const absId = - relationId ;
135150
136- const componentId = Math . floor ( absId / RELATION_SHIFT ) as EntityId ;
137- const targetId = ( absId % RELATION_SHIFT ) as EntityId ;
151+ const componentId = Math . floor ( absId / RELATION_SHIFT ) as ComponentId < any > ;
152+ const targetId = ( absId % RELATION_SHIFT ) as EntityId < any > ;
138153
139154 // Determine type based on targetId range
140155 if ( targetId === WILDCARD_TARGET_ID ) {
@@ -188,11 +203,17 @@ export function getIdType(
188203 * @param id The EntityId to analyze
189204 * @returns Detailed type information including relation subtypes
190205 */
191- export function getDetailedIdType ( id : EntityId < any > ) : {
192- type : "component" | "entity" | "entity-relation" | "component-relation" | "wildcard-relation" | "invalid" ;
193- componentId ?: EntityId < any > ;
194- targetId ?: EntityId < any > ;
195- } {
206+ export function getDetailedIdType ( id : EntityId < any > ) :
207+ | {
208+ type : "component" | "entity" | "invalid" ;
209+ componentId ?: never ;
210+ targetId ?: never ;
211+ }
212+ | {
213+ type : "entity-relation" | "component-relation" | "wildcard-relation" ;
214+ componentId : ComponentId < any > ;
215+ targetId : EntityId < any > ;
216+ } {
196217 if ( isComponentId ( id ) ) {
197218 return { type : "component" } ;
198219 }
@@ -351,13 +372,13 @@ export class ComponentIdManager {
351372 * Allocate a new component ID
352373 * Increments counter sequentially from 1
353374 */
354- allocate < T = void > ( ) : EntityId < T > {
375+ allocate < T = void > ( ) : ComponentId < T > {
355376 if ( this . nextId > COMPONENT_ID_MAX ) {
356377 throw new Error ( `Component ID overflow: maximum ${ COMPONENT_ID_MAX } components allowed` ) ;
357378 }
358379 const id = this . nextId ;
359380 this . nextId ++ ;
360- return id as EntityId < T > ;
381+ return id as ComponentId < T > ;
361382 }
362383
363384 /**
0 commit comments