Skip to content

Commit d46b2e8

Browse files
committed
feat(typed): support copy types in typed validators/converters
1 parent 2233b38 commit d46b2e8

File tree

4 files changed

+171
-2
lines changed

4 files changed

+171
-2
lines changed

lib/typed.js

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1102,6 +1102,29 @@ ${values.map(([from, to]) => ` if (obj === ${to}) {
11021102
return
11031103
}
11041104

1105+
if ('copy' in typeDef) {
1106+
if (typeof typeDef.copy.fromType !== 'string') {
1107+
throw new Error(`Copy type "${typeName}" needs a "fromType" string`)
1108+
}
1109+
1110+
// Look up the referenced type
1111+
const fromType = typeDef.copy.fromType
1112+
if (!this.typeTransformers[fromType] || !this.reprTransformers[fromType]) {
1113+
// Need to process the referenced type first
1114+
const fromTypeDef = this.schema.types[fromType]
1115+
if (!fromTypeDef) {
1116+
throw new Error(`Copy type "${typeName}" references unknown type "${fromType}"`)
1117+
}
1118+
this.addType(fromType, fromTypeDef)
1119+
}
1120+
1121+
// Copy transformers from the referenced type
1122+
this.typeTransformers[typeName] = this.typeTransformers[fromType]
1123+
this.reprTransformers[typeName] = this.reprTransformers[fromType]
1124+
1125+
return
1126+
}
1127+
11051128
throw new Error(`Can't deal with type kind: "${kind}"`)
11061129
}
11071130
}

test/test-typed-copy.js

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
/* eslint-env mocha */
2+
3+
import { fromDSL } from '../lib/from-dsl.js'
4+
import { create } from '../lib/typed.js'
5+
import { assert } from 'chai'
6+
7+
describe('Copy types', () => {
8+
it('should support copy types with basic types', () => {
9+
const schema = fromDSL(`type UserID = String
10+
type Age = Int
11+
type Balance = Float
12+
type Data = Bytes`)
13+
14+
const typed = create(schema, 'UserID')
15+
16+
// Test UserID (copy of String)
17+
assert.strictEqual(typed.toTyped('test-user-123'), 'test-user-123')
18+
assert.strictEqual(typed.toRepresentation('test-user-123'), 'test-user-123')
19+
assert.isUndefined(typed.toTyped(123))
20+
assert.isUndefined(typed.toTyped(null))
21+
22+
// Test Age (copy of Int)
23+
const ageTyped = create(schema, 'Age')
24+
assert.strictEqual(ageTyped.toTyped(25), 25)
25+
assert.strictEqual(ageTyped.toRepresentation(25), 25)
26+
assert.isUndefined(ageTyped.toTyped('25'))
27+
assert.isUndefined(ageTyped.toTyped(25.5))
28+
29+
// Test Balance (copy of Float)
30+
const balanceTyped = create(schema, 'Balance')
31+
assert.strictEqual(balanceTyped.toTyped(123.45), 123.45)
32+
assert.strictEqual(balanceTyped.toRepresentation(123.45), 123.45)
33+
assert.isUndefined(balanceTyped.toTyped('123.45'))
34+
35+
// Test Data (copy of Bytes)
36+
const dataTyped = create(schema, 'Data')
37+
const bytes = new Uint8Array([1, 2, 3, 4])
38+
assert.deepEqual(dataTyped.toTyped(bytes), bytes)
39+
assert.deepEqual(dataTyped.toRepresentation(bytes), bytes)
40+
assert.isUndefined(dataTyped.toTyped([1, 2, 3, 4]))
41+
})
42+
43+
it('should support copy types with complex types', () => {
44+
const schema = fromDSL(`type Person struct {
45+
name String
46+
age Int
47+
}
48+
49+
type Employee = Person
50+
51+
type Status enum {
52+
| Active
53+
| Inactive
54+
| Pending
55+
} representation string
56+
57+
type UserStatus = Status`)
58+
59+
// Test Employee (copy of Person struct)
60+
const employeeTyped = create(schema, 'Employee')
61+
const person = { name: 'Alice', age: 30 }
62+
assert.deepEqual(employeeTyped.toTyped(person), person)
63+
assert.deepEqual(employeeTyped.toRepresentation(person), person)
64+
assert.isUndefined(employeeTyped.toTyped({ name: 'Bob' })) // missing age
65+
assert.isUndefined(employeeTyped.toTyped({ name: 'Bob', age: '30' })) // wrong type for age
66+
67+
// Test UserStatus (copy of Status enum)
68+
const statusTyped = create(schema, 'UserStatus')
69+
assert.strictEqual(statusTyped.toTyped('Active'), 'Active')
70+
assert.strictEqual(statusTyped.toRepresentation('Active'), 'Active')
71+
assert.strictEqual(statusTyped.toTyped('Inactive'), 'Inactive')
72+
assert.isUndefined(statusTyped.toTyped('Unknown'))
73+
assert.isUndefined(statusTyped.toTyped(123))
74+
})
75+
76+
it('should support chained copy types', () => {
77+
const schema = fromDSL(`type ID = String
78+
type UserIdentifier = ID
79+
type AdminID = UserIdentifier`)
80+
81+
const adminTyped = create(schema, 'AdminID')
82+
assert.strictEqual(adminTyped.toTyped('admin-123'), 'admin-123')
83+
assert.strictEqual(adminTyped.toRepresentation('admin-123'), 'admin-123')
84+
assert.isUndefined(adminTyped.toTyped(123))
85+
assert.isUndefined(adminTyped.toTyped(null))
86+
})
87+
88+
it('should support copy types with list and map types', () => {
89+
const schema = fromDSL(`type Names [String]
90+
type TeamMembers = Names
91+
92+
type Settings {String: String}
93+
type Configuration = Settings`)
94+
95+
// Test TeamMembers (copy of Names list)
96+
const teamTyped = create(schema, 'TeamMembers')
97+
const names = ['Alice', 'Bob', 'Charlie']
98+
assert.deepEqual(teamTyped.toTyped(names), names)
99+
assert.deepEqual(teamTyped.toRepresentation(names), names)
100+
assert.isUndefined(teamTyped.toTyped(['Alice', 123, 'Bob'])) // invalid element type
101+
assert.isUndefined(teamTyped.toTyped('Alice')) // not a list
102+
103+
// Test Configuration (copy of Settings map)
104+
const configTyped = create(schema, 'Configuration')
105+
const settings = { theme: 'dark', language: 'en' }
106+
assert.deepEqual(configTyped.toTyped(settings), settings)
107+
assert.deepEqual(configTyped.toRepresentation(settings), settings)
108+
assert.isUndefined(configTyped.toTyped({ theme: 'dark', count: 5 })) // invalid value type
109+
assert.isUndefined(configTyped.toTyped('settings')) // not a map
110+
})
111+
112+
it('should handle forward references in copy types', () => {
113+
const schema = fromDSL(`type B = A
114+
type A = String`)
115+
116+
const bTyped = create(schema, 'B')
117+
assert.strictEqual(bTyped.toTyped('test'), 'test')
118+
assert.strictEqual(bTyped.toRepresentation('test'), 'test')
119+
assert.isUndefined(bTyped.toTyped(123))
120+
})
121+
122+
it('should throw error for unknown type references', () => {
123+
const schema = fromDSL('type A = UnknownType')
124+
125+
assert.throws(() => {
126+
create(schema, 'A')
127+
}, /Copy type "A" references unknown type "UnknownType"/)
128+
})
129+
130+
it('should throw error for invalid copy type definition', () => {
131+
const schema = {
132+
types: {
133+
A: {
134+
copy: {
135+
// missing fromType
136+
}
137+
}
138+
}
139+
}
140+
141+
assert.throws(() => {
142+
// @ts-ignore - Testing invalid schema
143+
create(schema, 'A')
144+
}, /Copy type "A" needs a "fromType" string/)
145+
})
146+
})

types/lib/typed.d.ts.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

types/tsconfig.tsbuildinfo

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"root":["../example-typed.js","../schema-schema.ts","../types.d.ts","../bin/cli.js","../bin/collect-input.js","../bin/json-to-schema.js","../bin/to-js.js","../bin/to-json.js","../bin/to-schema.js","../bin/to-tsdefs.js","../bin/util.js","../bin/validate.js","../lib/from-dsl.js","../lib/gen.js","../lib/schema-schema.js","../lib/to-dsl.js","../lib/typed.js","../lib/util.js","../lib/gen/go.js","../lib/gen/rust.js","../lib/gen/typescript.js","../test/_split-schema-schema.js","../test/test-bulk-fixtures.js","../test/test-cli.js","../test/test-examples.js","../test/test-gen.js","../test/test-schema-schema.js","../test/test-typed-any.js","../test/test-typed-basics.js","../test/test-typed-custom-transforms.js","../test/test-typed-enum.js","../test/test-typed-errors.js","../test/test-typed-struct.js","../test/test-typed-union.js","../test/typed-util.js","../test/webpack.config.js"],"version":"5.8.3"}
1+
{"root":["../example-typed.js","../schema-schema.ts","../types.d.ts","../bin/cli.js","../bin/collect-input.js","../bin/json-to-schema.js","../bin/to-js.js","../bin/to-json.js","../bin/to-schema.js","../bin/to-tsdefs.js","../bin/util.js","../bin/validate.js","../lib/from-dsl.js","../lib/gen.js","../lib/schema-schema.js","../lib/to-dsl.js","../lib/typed.js","../lib/util.js","../lib/gen/go.js","../lib/gen/rust.js","../lib/gen/typescript.js","../test/_split-schema-schema.js","../test/test-bulk-fixtures.js","../test/test-cli.js","../test/test-examples.js","../test/test-gen.js","../test/test-schema-schema.js","../test/test-typed-any.js","../test/test-typed-basics.js","../test/test-typed-copy.js","../test/test-typed-custom-transforms.js","../test/test-typed-enum.js","../test/test-typed-errors.js","../test/test-typed-struct.js","../test/test-typed-union.js","../test/typed-util.js","../test/webpack.config.js"],"version":"5.8.3"}

0 commit comments

Comments
 (0)