Open MaryGao opened 2 months ago
For generated enum we need enum member name and value, and the typespec member name may not be a valid typescript name so we need to normalize them and also normalization could help produce more ts-style SDKs.
What is a valid enum member name?
Enums are real objects that exist at runtime. Similar to object we could access enum member value via dot-notation e.g EnumA.X
or via indexed access types e.g EnumA["X"]
. Any literal which is a valid object property could be a valid enum member name expect numeric name e.g "10" or 10.
An enum member cannot have a numeric name.
What is the recommanded style for member name?
I think it's better to have PascalCase
style for enum member name and this is also style we used in HLC and aligned with the camelCase
style for property normalization.
Do we need to ensure all enum member could be dot-notable?
I prefer we try our best to do this but there may be situation where it's better to keep original constants other than giving a wired name. For example for float literal "1.2" to make it dot-notable we need to rename as "One2" or "1Dot2" or "12", other than that I would prefer to name it as "Number1.2"
and log a warning for names which are not dot-notable. But the bottle line would be generating compile-pass code.
Can we allow users to disable the name normalization?
I prefer yes. Similar to property norm we have an option property-name-normalized
to disable default normalization. I think we could make this option more generic like name-normalized
to disable norms for enum member name. But to generate compile-pass code we still need to handle numeric name.
What if we produce duplicated names? I don't want to introduce a lot complexity from our side. If we produce duplicated names we could guide users to rename the ambiguous names or turn off above option to disable norms.
Will it have breakings for HLC and how to handle them?
Yes, we will have breakings for numeric name, and we could use @clientName
to avoid this breaking.
Enum member name could be arbitrary or numeric. And the process to handle them would be:
Number${input}
.See the explicit logic to detect a numeric name; log a warnning for renaming by clientName decorator;PascalCase
styles;Number${input}
and log a warning for renaming by clientName decorator.// case 1: pure alphabets
["pascal", "Pascal"],
["pascalCase", "PascalCase"],
["PascalCase", "PascalCase"],
["pascalcase", "Pascalcase"],
["Pascalcase", "Pascalcase"],
// case 2: alphabets + special chars e.g .-*/
["pascal_case", "PascalCase"],
["pascal_case_", "PascalCase"],
["_pascal_case", "PascalCase"],
["pascal, case", "PascalCase"],
["MAX_of_MLD", "MaxOfMld"],
// case 3: alphabets + special chars + digits
["___pascal____case6666", "PascalCase6666"],
["_10Min", "Number10Min"],
// case 4: digits but not a numeric name
["090", "Number090"],
// case 5: digits but valid numeric name
["10", "Number10"],
["1.0", "Number1.0"],
["-1.0", "Number-1.0"],
@qiaozha @joheredi Could you help review above design? Thanks!
I don't think we can get dot user experience with “10Min” even if can be defined in TypeScript playground
why not just add Number prefix to all enum keys that are started with number? @joheredi how do you think?
I don't think we can get dot user experience with “10Min” even if can be defined in TypeScript playground
why not just add Number prefix to all enum keys that are started with number? @joheredi how do you think?
I am okay with adding a prefix with starting with number. I just want to clarify "090", "_090" and "90" are different cases and only the last one "90" is a valid numeric name. The first two would be not. And so any Number
prefix is adding after normalizaiton for first two.
Updated the above design with this idea.
@joheredi Could you help review the minor breakings introduced by this design? @qiaozha and I think the followings would introduce more readable codes so we take these changes.
Breaking change review
In this normalization we adopt the basic M4 logic except that we improve with following two parts which may cause breakings
num
prefix(prefix is configurable so for api version enum it is v
e.g V2024-07-01-Preview) to ensure this enum member is compilable.input
union ExtensibleString {
"2024_07_01_preview": "2024-07-01-preview",
_10Pascal: "_10Pascal",
`090`: "090",
`10`: "10",
`20`: "20",
`1.0`: "1.0",
`-2.0`: "-2.0",
string,
}
before
export enum KnownExtensibleString {
TwoThousandTwentyFour0701Preview = "2024-07-01-preview",
// invalid enum member
10Pascal = "_10Pascal",
Ninety= "090",
Ten = "10",
Num20 = "20",
One0 = "1.0",
// invalid enum member
20 = "-2.0",
}
now
export enum KnownExtensibleString {
"Num2024_07_01_Preview" = "2024-07-01-preview",
_10Pascal = "_10Pascal",
Num090 = "090",
Num10 = "10",
Num20 = "20",
"Num1.0" = "1.0",
"Num-1.0" = "-2.0",
}
// for version enum
export enum KnownVersions {
V2024_07_01_Preview = "2024-07-01-preview"
}
How to mitigate
We could always use clientName decorator to rename them if we don't want to take this.
Currently we directly generate the enum member name as typespec defined. This would be an issue if typespec enum member is not a valid typescript name. And it's better include to normalize them and also normalization could help produce more ts-style SDKs.
https://github.com/Azure/autorest.typescript/blob/ca2912409b91c4adff8f53215567d17c2c8fb5a8/packages/typespec-ts/test/modularIntegration/generated/azure/resource-manager/common-properties/src/index.d.ts#L50-L55