Skip to content

Commit 02aa9ce

Browse files
committed
feat: Support generating generics in interfaces
1 parent c3f34ad commit 02aa9ce

File tree

1 file changed

+76
-8
lines changed

1 file changed

+76
-8
lines changed

scripts/apitypings/main.go

Lines changed: 76 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,9 @@ func main() {
3939
// TypescriptTypes holds all the code blocks created.
4040
type TypescriptTypes struct {
4141
// Each entry is the type name, and it's typescript code block.
42-
Types map[string]string
43-
Enums map[string]string
42+
Types map[string]string
43+
Enums map[string]string
44+
Generics map[string]string
4445
}
4546

4647
// String just combines all the codeblocks.
@@ -50,16 +51,21 @@ func (t TypescriptTypes) String() string {
5051

5152
sortedTypes := make([]string, 0, len(t.Types))
5253
sortedEnums := make([]string, 0, len(t.Enums))
54+
sortedGenerics := make([]string, 0, len(t.Generics))
5355

5456
for k := range t.Types {
5557
sortedTypes = append(sortedTypes, k)
5658
}
5759
for k := range t.Enums {
5860
sortedEnums = append(sortedEnums, k)
5961
}
62+
for k := range t.Generics {
63+
sortedGenerics = append(sortedGenerics, k)
64+
}
6065

6166
sort.Strings(sortedTypes)
6267
sort.Strings(sortedEnums)
68+
sort.Strings(sortedGenerics)
6369

6470
for _, k := range sortedTypes {
6571
v := t.Types[k]
@@ -73,6 +79,12 @@ func (t TypescriptTypes) String() string {
7379
_, _ = s.WriteRune('\n')
7480
}
7581

82+
for _, k := range sortedGenerics {
83+
v := t.Generics[k]
84+
_, _ = s.WriteString(v)
85+
_, _ = s.WriteRune('\n')
86+
}
87+
7688
return strings.TrimRight(s.String(), "\n")
7789
}
7890

@@ -129,6 +141,7 @@ func (g *Generator) parsePackage(ctx context.Context, patterns ...string) error
129141
// generateAll will generate for all types found in the pkg
130142
func (g *Generator) generateAll() (*TypescriptTypes, error) {
131143
structs := make(map[string]string)
144+
generics := make(map[string]string)
132145
enums := make(map[string]types.Object)
133146
enumConsts := make(map[string][]*types.Const)
134147

@@ -170,12 +183,11 @@ func (g *Generator) generateAll() (*TypescriptTypes, error) {
170183
if !ok {
171184
panic("all typename should be named types")
172185
}
173-
switch named.Underlying().(type) {
186+
switch underNamed := named.Underlying().(type) {
174187
case *types.Struct:
175188
// type <Name> struct
176189
// Structs are obvious.
177-
st, _ := obj.Type().Underlying().(*types.Struct)
178-
codeBlock, err := g.buildStruct(obj, st)
190+
codeBlock, err := g.buildStruct(obj, underNamed)
179191
if err != nil {
180192
return nil, xerrors.Errorf("generate %q: %w", obj.Name(), err)
181193
}
@@ -205,7 +217,35 @@ func (g *Generator) generateAll() (*TypescriptTypes, error) {
205217
str.WriteString(fmt.Sprintf("export type %s = %s\n", obj.Name(), ts.ValueType))
206218
structs[obj.Name()] = str.String()
207219
case *types.Array, *types.Slice:
208-
// TODO: @emyrk if you need this, follow the same design as "*types.Map" case.
220+
// TODO: @emyrk if you need this, follow the same design as "*types.Map" case.
221+
case *types.Interface:
222+
// Interfaces are used as generics. Non-generic interfaces are
223+
// not supported.
224+
if underNamed.NumEmbeddeds() == 1 {
225+
union, ok := underNamed.EmbeddedType(0).(*types.Union)
226+
if !ok {
227+
// If the underlying is not a union, but has 1 type. It's
228+
// just that one type.
229+
union = types.NewUnion([]*types.Term{
230+
// Set the tilde to true to support underlying.
231+
// Doesn't actually affect our generation.
232+
types.NewTerm(true, underNamed.EmbeddedType(0)),
233+
})
234+
}
235+
236+
block, err := g.buildUnion(obj, union)
237+
if err != nil {
238+
return nil, xerrors.Errorf("generate union %q: %w", obj.Name(), err)
239+
}
240+
generics[obj.Name()] = block
241+
}
242+
case *types.Signature:
243+
// Ignore named functions.
244+
default:
245+
// If you hit this error, you added a new unsupported named type.
246+
// The easiest way to solve this is add a new case above with
247+
// your type and a TODO to implement it.
248+
return nil, xerrors.Errorf("unsupported named type %q", underNamed.String())
209249
}
210250
case *types.Var:
211251
// TODO: Are any enums var declarations? This is also codersdk.Me.
@@ -242,8 +282,9 @@ func (g *Generator) generateAll() (*TypescriptTypes, error) {
242282
}
243283

244284
return &TypescriptTypes{
245-
Types: structs,
246-
Enums: enumCodeBlocks,
285+
Types: structs,
286+
Enums: enumCodeBlocks,
287+
Generics: generics,
247288
}, nil
248289
}
249290

@@ -252,6 +293,33 @@ func (g *Generator) posLine(obj types.Object) string {
252293
return fmt.Sprintf("// From %s\n", filepath.Join("codersdk", filepath.Base(file.Name())))
253294
}
254295

296+
// buildStruct just prints the typescript def for a type.
297+
func (g *Generator) buildUnion(obj types.Object, st *types.Union) (string, error) {
298+
var s strings.Builder
299+
_, _ = s.WriteString(g.posLine(obj))
300+
301+
allTypes := make([]string, 0, st.Len())
302+
var optional bool
303+
for i := 0; i < st.Len(); i++ {
304+
term := st.Term(i)
305+
scriptType, err := g.typescriptType(term.Type())
306+
if err != nil {
307+
return "", xerrors.Errorf("union %q for %q failed to get type: %w", st.String(), obj.Name(), err)
308+
}
309+
allTypes = append(allTypes, scriptType.ValueType)
310+
optional = optional || scriptType.Optional
311+
}
312+
313+
qMark := ""
314+
if optional {
315+
qMark = "?"
316+
}
317+
318+
s.WriteString(fmt.Sprintf("export type %s%s = %s\n", obj.Name(), qMark, strings.Join(allTypes, " | ")))
319+
320+
return s.String(), nil
321+
}
322+
255323
// buildStruct just prints the typescript def for a type.
256324
func (g *Generator) buildStruct(obj types.Object, st *types.Struct) (string, error) {
257325
var s strings.Builder

0 commit comments

Comments
 (0)