-
Notifications
You must be signed in to change notification settings - Fork 760
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
User-defined types -How to define default value #9636
Comments
Default values on types came up at a previous team discussion, and the consensus at the time was that modifying a parameter or output based on its declared type wasn't something that should be done lightly, and the rules around which default value to apply and where defaults would be permitted got tricky very quickly. This is some strawman syntax from that discussion: type barString 'bar'|'baz' = 'bar'
@sealed()
type myObject {
@minLength(3)
foo: string = 'foo'
bar: barString // <-- uses default value from type
baz: barString = 'baz' // <-- uses explicit default
recur?: myObject // <-- recursive, so blocks a default for `myObject`
} // '= {}' would be a compiler error either:
// because 'myObject' is directly recursive (if we apply property defaults to the object default), or
// because required properties 'foo', 'bar', and 'baz' are missing We looked at a few schema definitions languages that support some form of default value, and found that most schema definition languages either treat default values as purely informational or had some rules that made usage confusing:
As an alternative, a template author can always supply a default when they read a value. #9454 should help with this, as nullable types will be usable with the |
@jeskew Probably I should have explained it better. My main goal is not to define a default value in type so that default value to be used later in the Bicep deployment. I already have a mechanism in Bicep code where we are setting default values for certain properties if they are not defined. My main goal is that when folks use intellisense to build parameters file or use the module to see foo is a boolean and has default value of true. As you see above foo is reported as Type: true. I want to define foo in a way that it shows Type: bool with default value of true. With the example of type barString seems this can be achieved but only for enums. type defaultTrue = bool
type test = {
foo: defaultTrue = true
}
but it gives error on the equal sign to true but I guess this is something you are planning to implement. That looks ok to me. |
I should clarify that the strawman syntax from my previous comment is not planned to be implemented, though that could be revisited based on community feedback. For the use case you describe where you are setting the default value but wish to communicate this to users, could you use a |
@jeskew yes, description was my workaround but it is always good intellisense to show this separately from the description and also to be visible in code so when you make changes it is clear what needs to be changed. For example one version of the template foo can have default true but in another to be false. |
@jeskew
What I'm trying to achieve, is to move this:
inside a user-data type that contains all the storageProfile fields. |
@pie-r While you can't define a default value for a user-defined type at this time, you can make the property optional and apply a default value when reading it: type sizeGbType = int
type storageProfileType = {
@description('Optional. The storage size of the server.')
sizeGB: int?
...
}
param storageProfile storageProfileType
var defaultSizeGB = 32
resource disk 'Microsoft.Compute/disks@2022-07-02' = {
...
properties: {
diskSizeGB = storageProfile.?sizeGB ?? defaultSizeGB
...
}
} |
In this specific case, I think that writing 4 different statement, instead of a single row with the single scalar is not worth it.
But I see use cases where your solution is the proper approach. Thanks for sharing! |
My syntax suggestion to enable default values in the context of user-defined types is to have the ability to set them in the That means define a type as: type storageProfileType = {
key1: string
sizeGB: int
key2: string
} A param like: param storageProfile storageProfileType = {
key1: ='xxx'
sizeGB: = 32 # default, use this value if not overridden from inputparam
key2: ='yyy'
} And now given an input storageProfile = {
key1: 'fooo'
} At build time bicep convert it with: param storageProfile storageProfileType = {
key1: 'fooo'
sizeGB: 32 --> use default
key2: 'yyy' --> use default
} OR given an input storageProfile = {
key1: 'fooo'
sizeGB: 16
} At build time bicep convert it with: param storageProfile storageProfileType = {
key1: 'fooo'
sizeGB: 16
key2: 'yyy' --> use default
} |
Porting some syntax suggestions for this from #12661: (original author: @mattias-fjellstrom)
|
One other option would be to add some form of param myParameter {
field1: string
field2: string?
}
var myParameterDefaults = {
field2: 'default value'
}
var withDefaults = deepMerge(myParameterDefaults, myParameter) |
This one seems the most easy one to use to me: param myParameter {
field1: string
field2: string? = 'default value'
} Sometimes the default value is something that can only be calculated at deployment time. For example the subscription ID so being able to just type the words: 'current subscription' instead the actual GUID as that one can only known at deployment time is good for us. So basically being relaxed as much as possible syntax. And of course when you use intellisense that default value to be listed along the type and the description with some text like: Default value: current subscription. |
It turns out that the param myParameter {
field1: string
field2: string?
}
var myParameterDefaults = {
field2: 'default value'
}
var withDefaults = union(myParameterDefaults, myParameter) |
@jeskew That's a good workaround, I had not thought of trying that 👍🏻 |
Be careful with union(). It does not merge arrays within object and null is never merged (if you have default value 'str' and you provide null, the end value will be 'str'. |
I'd rather have my type be smarter rather than my code... This is primarily to match what some resource providers do currently. What happens when we get to import types from resource providers and they have default values? |
Is there any progress of defining defaults? |
This feels like it would need to introduce strongly typed variable and param creation. Otherwise how would the compiler know what set of defaults to apply? Given 2 types: type KvConfig = {
name: string
foo: string = 'foo'
}
type StorageConfig = {
name: string
bar: string = 'bar'
} How would the compiler assign defaults to the following: var kvconf = { name: 'kv' } // <-- user probably wants to resolve to KvConfig
var saconf = { name: 'storage' } // <-- user probably wants to resolve to StorageConfig
var nameconf = { name: 'myname' } // <-- user maybe wants just an object What we can do today is define a "constructor" for the // myObject.bicep
type MyObject = {
@minLength(3)
foo: string?
bar: string?
baz: string?
recur: myObject?
}
func new(userObj object) MyObject => union({
foo: 'foo'
bar: 'bar'
baz: 'baz'
}, userObj)
// main.bicep
import * as MyObject from './myObject.bicep'
var myObj = MyObject.new({foo: 'foo'}) // => returns a MyObject.MyObject but is fragile:
Proposal
To use the examples above it would look like // configs.bicep
type KV = {
name: string
@default('foo')
foo: string
}
type Storage = {
name: string
@default('bar')
bar: string
}
// main.bicep
import * as Configs from './configs.bicep'
var kvconf1 = Config.KV.new(name: 'kv', foo: 'baz') // => { name: 'kv', foo: 'baz'}
var kvconf2 = Config.KV.new(name: 'kv') // => { name: 'kv', foo: 'foo'}
var saconf = Config.Storage.new(name: 'storage') // => { name: 'storage', bar: 'bar'}
var nameconf = { name: 'myname' } // => { name: 'myname' } I'm not sure how much impact this would have on the underlying ARM, but this would alleviate some of the ask above, without needing to introduce persistent type associations to objects. |
Is your feature request related to a problem? Please describe.
We can define the following type:
But when you use intellisense on the foo property you get:
Instead what I want to get is that foo is boolean and default value is true.
What if I also want to define default value that uses Bicep function like:
Describe the solution you'd like
See above.
The text was updated successfully, but these errors were encountered: