An experimental TypeSpec syntax for Lexicon

fix types

authored by danabra.mov and committed by Tangled e9dc14c6 0c277f30

Changed files
+22 -10
packages
emitter
+22 -10
packages/emitter/src/emitter.ts
··· 48 48 LexCidLink, 49 49 LexRefVariant, 50 50 LexToken, 51 + LexBoolean, 52 + LexInteger, 53 + LexString, 51 54 } from "./types.js"; 52 55 53 56 import { ··· 415 418 // Apply @default decorator if present 416 419 const rawDefault = getDefault(this.program, scalar); 417 420 const defaultValue = this.processDefaultValue(rawDefault); 418 - let defWithDefault: any = { ...scalarDef }; 421 + let defWithDefault: LexObjectProperty = { ...scalarDef }; 419 422 420 423 if (defaultValue !== undefined) { 421 424 // Check if it's a Type (model reference for tokens) ··· 431 434 } else { 432 435 // Validate that the default value matches the type 433 436 this.assertValidValueForType(scalarDef.type, defaultValue, scalar); 434 - defWithDefault = { ...defWithDefault, default: defaultValue }; 437 + // Type-safe narrowing based on both the type discriminator and value type 438 + if (scalarDef.type === "boolean" && typeof defaultValue === "boolean") { 439 + (defWithDefault as LexBoolean).default = defaultValue; 440 + } else if (scalarDef.type === "integer" && typeof defaultValue === "number") { 441 + (defWithDefault as LexInteger).default = defaultValue; 442 + } else if (scalarDef.type === "string" && typeof defaultValue === "string") { 443 + (defWithDefault as LexString).default = defaultValue; 444 + } 435 445 } 436 446 } 437 447 ··· 439 449 if (scalarDef.type === "integer") { 440 450 const minValue = getMinValue(this.program, scalar); 441 451 if (minValue !== undefined) { 442 - (defWithDefault as any).minimum = minValue; 452 + (defWithDefault as LexInteger).minimum = minValue; 443 453 } 444 454 const maxValue = getMaxValue(this.program, scalar); 445 455 if (maxValue !== undefined) { 446 - (defWithDefault as any).maximum = maxValue; 456 + (defWithDefault as LexInteger).maximum = maxValue; 447 457 } 448 458 } 449 459 ··· 474 484 // Apply @default decorator if present 475 485 const rawDefault = getDefault(this.program, union); 476 486 const defaultValue = this.processDefaultValue(rawDefault); 477 - let defWithDefault: any = { ...unionDef }; 487 + let defWithDefault: LexString = { ...unionDef as LexString }; 478 488 479 489 if (defaultValue !== undefined) { 480 490 // Check if it's a Type (model reference for tokens) ··· 517 527 `Use @inline to inline them at usage sites, use @token models for known values, or use string literals.`, 518 528 target: union, 519 529 }); 520 - } else if (unionDef.type === "integer" && (unionDef as any).enum) { 530 + } else if (unionDef.type === "integer" && (unionDef as LexInteger).enum) { 521 531 // Integer enums can also be defs 522 532 const defName = name.charAt(0).toLowerCase() + name.slice(1); 523 533 const description = getDoc(this.program, union); ··· 525 535 // Apply @default decorator if present 526 536 const rawDefault = getDefault(this.program, union); 527 537 const defaultValue = this.processDefaultValue(rawDefault); 528 - let defWithDefault = { ...unionDef }; 538 + let defWithDefault: LexInteger = { ...unionDef as LexInteger }; 529 539 530 540 if (defaultValue !== undefined) { 531 541 if (typeof defaultValue === "number") { ··· 645 655 // Check for default value: property default takes precedence, then union's @default 646 656 let defaultValue: string | number | boolean | undefined; 647 657 if (prop?.defaultValue !== undefined) { 648 - defaultValue = serializeValueAsJson(this.program, prop.defaultValue, prop) as any; 658 + defaultValue = serializeValueAsJson(this.program, prop.defaultValue, prop) as string | number | boolean; 649 659 } else { 650 660 // If no property default, check union's @default decorator 651 661 const rawUnionDefault = getDefault(this.program, unionType); ··· 681 691 // Check for default value: property default takes precedence, then union's @default 682 692 let defaultValue: string | number | boolean | undefined; 683 693 if (prop?.defaultValue !== undefined) { 684 - defaultValue = serializeValueAsJson(this.program, prop.defaultValue, prop) as any; 694 + defaultValue = serializeValueAsJson(this.program, prop.defaultValue, prop) as string | number | boolean; 685 695 } else { 686 696 // If no property default, check union's @default decorator 687 697 const rawUnionDefault = getDefault(this.program, unionType); ··· 1452 1462 const propDefault = serializeValueAsJson(this.program, prop.defaultValue, prop); 1453 1463 1454 1464 // For union defaults that are model references, we need to resolve them for comparison 1455 - let resolvedUnionDefault: string | number | boolean | undefined = unionDefault as any; 1465 + let resolvedUnionDefault: string | number | boolean | undefined; 1456 1466 if (unionDefault && typeof unionDefault === 'object' && 'kind' in unionDefault && unionDefault.kind === 'Model') { 1457 1467 const ref = this.getModelReference(unionDefault as Model, true); 1458 1468 resolvedUnionDefault = ref || undefined; 1469 + } else { 1470 + resolvedUnionDefault = unionDefault as string | number | boolean; 1459 1471 } 1460 1472 1461 1473 // If the union has a different default, or if the property has a default but the union doesn't, error