Skip to content

Commit 261e539

Browse files
committed
fix: type assertion for struct types
Signed-off-by: Christian Stewart <christian@aperture.us>
1 parent 8c8d86f commit 261e539

5 files changed

Lines changed: 12 additions & 299 deletions

File tree

compliance/WIP.md

Lines changed: 0 additions & 193 deletions
This file was deleted.

compliance/tests/selector_expr_ok_variable/selector_expr_ok_variable.gs.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,10 +45,10 @@ export async function main(): Promise<void> {
4545

4646
// This should trigger the error: ok expression is not an identifier: *ast.SelectorExpr
4747
// The 'ok' variable is result.ok (a selector expression) instead of a simple identifier
48-
let _gs_ta_val_: number
49-
let _gs_ta_ok_: boolean
50-
({ value: _gs_ta_val_, ok: _gs_ta_ok_ } = $.typeAssert<number>(x, {kind: $.TypeKind.Basic, name: 'number'}))
51-
result.ok = _gs_ta_ok_
48+
let _gs_ta_val_302_: number
49+
let _gs_ta_ok_302_: boolean
50+
({ value: _gs_ta_val_302_, ok: _gs_ta_ok_302_ } = $.typeAssert<number>(x, {kind: $.TypeKind.Basic, name: 'number'}))
51+
result.ok = _gs_ta_ok_302_
5252

5353
console.log("Type assertion successful:", result.ok)
5454
}

compliance/tests/type_assertion_duplicate_vars/actual.log

Lines changed: 0 additions & 2 deletions
This file was deleted.

compliance/tests/type_assertion_duplicate_vars/skip-test

Whitespace-only changes.

gs/builtin/type.ts

Lines changed: 8 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -459,24 +459,18 @@ function matchesBasicType(value: any, info: TypeInfo): boolean {
459459
function matchesStructType(value: any, info: TypeInfo): boolean {
460460
if (!isStructTypeInfo(info)) return false
461461

462-
// For structs, use instanceof with the constructor
462+
// For named struct types with constructors, use instanceof (nominal matching)
463463
if (info.ctor && value instanceof info.ctor) {
464464
return true
465465
}
466466

467-
// Check if the value has all methods defined in the struct's TypeInfo
468-
// This is a structural check, not a signature check here.
469-
// Signature checks are more relevant for interface satisfaction.
470-
if (info.methods && typeof value === 'object' && value !== null) {
471-
const allMethodsExist = info.methods.every(
472-
(methodSig) => typeof (value as any)[methodSig.name] === 'function',
473-
)
474-
if (!allMethodsExist) {
475-
return false
476-
}
477-
// Further signature checking could be added here if needed for struct-to-struct assignability
467+
// For named struct types with constructors, if instanceof fails, return false
468+
// This ensures named struct types use exact type matching
469+
if (info.ctor) {
470+
return false
478471
}
479472

473+
// For anonymous struct types (no constructor), use structural matching
480474
if (typeof value === 'object' && value !== null && info.fields) {
481475
const fieldNames = Object.keys(info.fields || {})
482476
const valueFields = Object.keys(value)
@@ -850,97 +844,12 @@ export function typeAssert<T>(
850844
typeInfo: string | TypeInfo,
851845
): TypeAssertResult<T> {
852846
const normalizedType = normalizeTypeInfo(typeInfo)
853-
854847
if (isPointerTypeInfo(normalizedType) && value === null) {
855848
return { value: null as unknown as T, ok: true }
856849
}
857850

858-
if (
859-
isStructTypeInfo(normalizedType) &&
860-
normalizedType.methods &&
861-
normalizedType.methods.length > 0 &&
862-
typeof value === 'object' &&
863-
value !== null
864-
) {
865-
// Check if the value implements all methods of the struct type with compatible signatures.
866-
// This is more for interface satisfaction by a struct.
867-
// For struct-to-struct assertion, usually instanceof or field checks are primary.
868-
const allMethodsMatch = normalizedType.methods.every(
869-
(requiredMethodSig) => {
870-
const actualMethod = (value as any)[requiredMethodSig.name]
871-
if (typeof actualMethod !== 'function') {
872-
return false
873-
}
874-
const valueTypeInfoVal = (value as any).$typeInfo
875-
if (valueTypeInfoVal) {
876-
const normalizedValueType = normalizeTypeInfo(valueTypeInfoVal)
877-
if (
878-
isStructTypeInfo(normalizedValueType) ||
879-
isInterfaceTypeInfo(normalizedValueType)
880-
) {
881-
const actualValueMethodSig = normalizedValueType.methods.find(
882-
(m) => m.name === requiredMethodSig.name,
883-
)
884-
if (actualValueMethodSig) {
885-
// Perform full signature comparison using MethodSignatures
886-
const paramsMatch = areMethodArgsArraysIdentical(
887-
requiredMethodSig.args,
888-
actualValueMethodSig.args,
889-
)
890-
const resultsMatch = areMethodArgsArraysIdentical(
891-
requiredMethodSig.returns,
892-
actualValueMethodSig.returns,
893-
)
894-
return paramsMatch && resultsMatch
895-
} else {
896-
// Value has TypeInfo listing methods, but this specific method isn't listed.
897-
// This implies a mismatch for strict signature check based on TypeInfo.
898-
return false
899-
}
900-
}
901-
}
902-
903-
// If the function exists and there's no type info, assume it matches.
904-
// TODO check this
905-
return true
906-
},
907-
)
908-
909-
if (allMethodsMatch) {
910-
return { value: value as T, ok: true }
911-
}
912-
}
913-
914-
if (
915-
isStructTypeInfo(normalizedType) &&
916-
normalizedType.fields &&
917-
typeof value === 'object' &&
918-
value !== null
919-
) {
920-
const fieldNames = Object.keys(normalizedType.fields)
921-
const valueFields = Object.keys(value)
922-
923-
// For struct type assertions, we need exact field matching
924-
const structFieldsMatch =
925-
fieldNames.length === valueFields.length &&
926-
fieldNames.every((field: string) => field in value) &&
927-
valueFields.every((field) => fieldNames.includes(field))
928-
929-
if (structFieldsMatch) {
930-
const typesMatch = Object.entries(normalizedType.fields).every(
931-
([fieldName, fieldType]) => {
932-
return matchesType(
933-
value[fieldName],
934-
normalizeTypeInfo(fieldType as TypeInfo | string),
935-
)
936-
},
937-
)
938-
939-
return { value: value as T, ok: typesMatch }
940-
} else {
941-
return { value: null as unknown as T, ok: false }
942-
}
943-
}
851+
// Removed struct matching logic - struct types should use nominal matching
852+
// via matchesStructType in matchesType, not structural matching here
944853

945854
if (
946855
isMapTypeInfo(normalizedType) &&
@@ -991,7 +900,6 @@ export function typeAssert<T>(
991900
}
992901

993902
const matches = matchesType(value, normalizedType)
994-
995903
if (matches) {
996904
return { value: value as T, ok: true }
997905
}

0 commit comments

Comments
 (0)