Skip to content

Commit b448e02

Browse files
committed
Add weakly typed enums aka alias constants
1 parent ad3327b commit b448e02

File tree

7 files changed

+79
-10
lines changed

7 files changed

+79
-10
lines changed

docs/src/content/docs/changelog.mdx

+1
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
5050
- Add binding generator support for `omitzero` JSON flag by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
5151
- Add `//wails:ignore` directive to prevent binding generation for chosen service methods by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
5252
- Add `//wails:internal` directive on services and models to allow for types that are exported in Go but not in JS/TS by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
53+
- Add binding generator support for constants of alias type to allow for weakly typed enums by [@fbbdev](https://github.com/fbbdev) in [#4045](https://github.com/wailsapp/wails/pull/4045)
5354

5455
### Fixed
5556

v3/internal/generator/collect/model.go

+20-4
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,24 @@ func (info *ModelInfo) Collect() *ModelInfo {
192192
// because in doing so we could end up with a private type from another package.
193193
def = t.Rhs()
194194

195+
// Test for constants with alias type,
196+
// but only when non-generic alias resolves to a basic type
197+
// (hence not to e.g. a named type).
198+
if basic, ok := types.Unalias(def).(*types.Basic); ok {
199+
if !isGeneric && basic.Info()&types.IsConstType != 0 && basic.Info()&types.IsComplex == 0 {
200+
// Non-generic alias resolves to a representable constant type:
201+
// look for defined constants whose type is exactly the alias typ.
202+
for _, name := range obj.Pkg().Scope().Names() {
203+
if cnst, ok := obj.Pkg().Scope().Lookup(name).(*types.Const); ok {
204+
alias, isAlias := cnst.Type().(*types.Alias)
205+
if isAlias && cnst.Val().Kind() != constant.Unknown && alias.Obj() == t.Obj() {
206+
constants = append(constants, cnst)
207+
}
208+
}
209+
}
210+
}
211+
}
212+
195213
case *types.Named:
196214
// Model is a named type:
197215
// jump directly to underlying type to match go semantics,
@@ -222,7 +240,7 @@ func (info *ModelInfo) Collect() *ModelInfo {
222240
info.Type = types.Typ[types.String]
223241
return
224242
}
225-
} else if basic, ok := def.(*types.Basic); ok {
243+
} else if basic, ok := def.Underlying().(*types.Basic); ok {
226244
// Test for enums (excluding marshalers and generic types).
227245
if !isGeneric && basic.Info()&types.IsConstType != 0 && basic.Info()&types.IsComplex == 0 {
228246
// Named type is defined as a representable constant type:
@@ -255,12 +273,10 @@ func (info *ModelInfo) Collect() *ModelInfo {
255273
info.Imports.AddType(def)
256274

257275
// Handle enum types.
258-
// constants slice is always empty for aliases, structs, marshalers.
276+
// constants slice is always empty for structs, marshalers.
259277
if len(constants) > 0 {
260278
// Collect information about enum values.
261279
info.collectEnum(constants)
262-
info.Type = def
263-
return
264280
}
265281

266282
// That's all, folks. Render as a TS alias.

v3/internal/generator/render/info.go

+4-2
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import (
88

99
// modelInfo gathers useful information about a model.
1010
type modelInfo struct {
11-
IsEnum bool
11+
HasValues bool
12+
IsEnum bool
1213

1314
IsAlias bool
1415
IsClassAlias bool
@@ -27,7 +28,8 @@ type modelInfo struct {
2728

2829
// modelinfo gathers and returns useful information about the given model.
2930
func modelinfo(model *collect.ModelInfo, useInterfaces bool) (info modelInfo) {
30-
info.IsEnum = len(model.Values) > 0
31+
info.HasValues = len(model.Values) > 0
32+
info.IsEnum = info.HasValues && !model.Alias
3133

3234
info.IsAlias = !info.IsEnum && model.Type != nil
3335
info.IsClassAlias = info.IsAlias && model.Predicates.IsClass && !useInterfaces

v3/internal/generator/render/templates/index.tmpl

+2-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export {
3939

4040
{{- $info := modelinfo $model $useInterfaces }}
4141

42-
{{- if or $info.IsEnum $info.IsClassAlias $info.IsClass}}
42+
{{- if or $info.HasValues $info.IsClassAlias $info.IsClass}}
4343
{{- if not $hasObjects}}
4444
{{- $hasObjects = true}}
4545
export {
@@ -67,7 +67,7 @@ import * as $models from "./{{js $models}}";
6767
{{- $info := modelinfo $model $useInterfaces }}
6868
{{- $template := $info.Template }}
6969

70-
{{- if or $info.IsEnum $info.IsClassAlias $info.IsClass}}{{continue}}{{end}}
70+
{{- if or $info.HasValues $info.IsClassAlias $info.IsClass}}{{continue}}{{end}}
7171

7272
{{- if $renderer.TS}}
7373
{{- if $hasTypes}},{{end}}

v3/internal/generator/render/templates/models.js.tmpl

+11-2
Original file line numberDiff line numberDiff line change
@@ -57,13 +57,22 @@ import * as {{jsimport .}} from "{{js .RelPath}}/{{js $models}}";
5757
{{- end}}
5858
*/
5959
{{- end}}
60-
{{- if $info.IsEnum}}
60+
{{- if $info.HasValues}}
61+
{{- if not $info.IsEnum}}
62+
63+
/**
64+
* Predefined constants for type {{jsid $model.Name}}.
65+
* @namespace
66+
*/
67+
{{- end}}
6168
export const {{jsid $model.Name}} = {
69+
{{- if $info.IsEnum}}
6270
/**
6371
* The Go zero value for the underlying type of the enum.
6472
*/
6573
$zero: {{$module.JSDefault $model.Type false}},
66-
{{range $i, $decl := $model.Values}}{{range $j, $spec := $decl}}{{range $k, $value := $spec}}
74+
{{end}}
75+
{{- range $i, $decl := $model.Values}}{{range $j, $spec := $decl}}{{range $k, $value := $spec}}
6776
{{- if and (ne $i 0) (eq $j 0) (eq $k 0)}}
6877
{{end}}
6978
{{- if or (and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)) (and (eq $k 0) (hasdoc $value.Spec.Doc))}}

v3/internal/generator/render/templates/models.ts.tmpl

+27
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,33 @@ export const {{jsid $model.Name}} = {{if istpalias $model.Type -}}
7878
export type {{jsid $model.Name}}{{$template.ParamList}} = {{$module.JSType $model.Type}};
7979
{{else if $info.IsTypeAlias}}
8080
export type {{jsid $model.Name}}{{$template.ParamList}} = {{$module.JSType $model.Type}};
81+
{{- if $info.HasValues}}
82+
83+
/**
84+
* Predefined constants for type {{jsid $model.Name}}.
85+
* @namespace
86+
*/
87+
export const {{jsid $model.Name}} = {
88+
{{- range $i, $decl := $model.Values}}{{range $j, $spec := $decl}}{{range $k, $value := $spec}}
89+
{{- if and (ne $i 0) (eq $j 0) (eq $k 0)}}
90+
{{end}}
91+
{{- if or (and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)) (and (eq $k 0) (hasdoc $value.Spec.Doc))}}
92+
{{- if gt $j 0}}
93+
{{end}}
94+
/**
95+
{{- if and (eq $j 0) (eq $k 0) (hasdoc $value.Decl.Doc)}}
96+
{{- jsdoc $value.Decl.Doc.Text " "}}{{if and (eq $k 0) (hasdoc $value.Spec.Doc)}}
97+
*{{end}}
98+
{{- end}}
99+
{{- if and (eq $k 0) (hasdoc $value.Spec.Doc)}}
100+
{{- jsdoc $value.Spec.Doc.Text " "}}
101+
{{- end}}
102+
*/
103+
{{- end}}
104+
{{jsid $value.Name}}: {{jsvalue $value.Value}},
105+
{{- end}}{{end}}{{end}}
106+
};
107+
{{- end}}
81108
{{else if $info.IsClassOrInterface}}
82109
export {{if $info.IsInterface}}interface{{else}}class{{end}} {{jsid $model.Name}}{{$template.ParamList}} {
83110
{{- range $i, $decl := $model.Fields}}{{range $j, $field := $decl}}

v3/internal/generator/testcases/enum/main.go

+14
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,19 @@ const (
1919
Dr Title = "Dr"
2020
)
2121

22+
// Age is an integer with some predefined values
23+
type Age = int
24+
25+
const (
26+
NewBorn Age = 0
27+
Teenager Age = 12
28+
YoungAdult Age = 18
29+
30+
// Oh no, some grey hair!
31+
MiddleAged Age = 50
32+
Mathusalem Age = 1000 // Unbelievable!
33+
)
34+
2235
// GreetService is great
2336
type GreetService struct {
2437
SomeVariable int
@@ -30,6 +43,7 @@ type GreetService struct {
3043
type Person struct {
3144
Title Title
3245
Name string
46+
Age Age
3347
}
3448

3549
// Greet does XYZ

0 commit comments

Comments
 (0)