-
Notifications
You must be signed in to change notification settings - Fork 759
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
Custom type imports #9311
Comments
Related to #9357 |
I'd be very interested in something like this as well. I've just this morning looked into I'm not sure of the inner workings of Bicep but I'm wondering if a typescript // types/Animals.bicep
type Cat = {
name: string
furStyle: string
age: int
}
type Elephant = {
name: string
trunkLength: int
toeNailsPainted: bool
}
export { Cat, Elephant } // deployCats.bicep
@import { Cat } from './types/Animals';
param cats: Cat[] // deployElephants.bicep
@import { Elephant } from './types/Animals';
param elephants: Elephant[] |
Yep gave custom types a whirl today, and hit the need for this immediately. We have modules that build on top of other modules, and as such sometimes those higher level modules want to naturally reference the types exposed by the lower level ones - however they can't define a custom type that leverages those lower level custom types. For example:
Here, the logic to deploy a single |
I really like this import anything idea, though variables might not be compile-time constants so in my mind it doesn't make sense to import them. Maybe a new Right now a I would instead suggest fundamentally changing what a This could for example look something like this: // landingZoneSubscription.bicep
include { Budget } from 'budget.bicep'
// define a type
type LzSubscriptionDefintion = {
applicationName: string
subscriptionName: string
addressPrefix: string
budget: Budget
}
// define a module
def LandingZoneSubscription tenant = {
// inside these brackes would basically be a copy-paste of an old-style .bicep file
@description('The Landing Zone Subscription\'s definition')
param defintion LzSubscriptionDefinition
resource subscriptionAlias 'Microsoft.Subscription/aliases@2021-10-01' = {
name: definition.subscriptionName
}
// more resources here
output subscriptionId string = subscriptionAlias.properties.subscriptionId
}
export { LzSubscriptionDefinition, LandingZoneSubscription }
// landingZones.bicep
include { LzSubscriptionDefinition as LzDef, LandingZoneSubscription as LzSub } from 'landingZoneSubscription.bicep'
def LandingZones tenant = {
@description('An array of definitions of all the Landing Zones to create')
param landingZoneDefinitions LzDef[]
module landingZones LzSub = [for lzDef in landingZoneDefinitions: {
name: `deploy-lz-${lzDef.subscriptionName}`
params: {
definition: lzDef
}
}]
} A backwards-compatible way would instead be C/C++-style header files that can only define types, constants and functions, which could then be imported by multiple // landingZoneSubscription.biceph
include { Budget } from 'budget.biceph'
// define a type
type LzSubscriptionDefintion = {
applicationName: string
subscriptionName: string
addressPrefix: string
budget: Budget
} // landingZoneSubscription.bicep
targetScope = 'tenant'
include { LzSubscriptionDefintion } from 'landingZoneSubscription.biceph'
// inside these brackes would basically be a copy-paste of an old-style .bicep file
@description('The Landing Zone Subscription\'s definition')
param defintion LzSubscriptionDefinition
resource subscriptionAlias 'Microsoft.Subscription/aliases@2021-10-01' = {
name: definition.subscriptionName
}
// more resources here
output subscriptionId string = subscriptionAlias.properties.subscriptionId // landingZones.bicep
targetScope = 'tenant'
include { LzSubscriptionDefinition as LzDef } from 'landingZoneSubscription.biceph'
@description('An array of definitions of all the Landing Zones to create')
param landingZoneDefinitions LzDef[]
module landingZones LzSub = [for lzDef in landingZoneDefinitions: {
name: `deploy-lz-${lzDef.subscriptionName}`
params: {
definition: lzDef
}
}] |
@pelle-domela I like the use of the Besides types and compile-time resolvable variables, one other construct that might be useful to share via |
I absolutely agree with @jeskew on import vs include. I also think having an include feature would be helpful for many things and it's absolutely something that would come in handy for user defined functions. |
I agree - I haven't done anything with the extensibility providers, so I hadn't realized that was already a reserved keyword. |
Because we're looking at using sharing functions, variables, and types at compile time, I opened the |
Hi WhitWaldo, this issue has been marked as stale because it was labeled as requiring author feedback but has not had any activity for 4 days. It will be closed if no further activity occurs within 3 days of this comment. Thanks for contributing to bicep! 😄 🦾 |
Well, in that case, bump! |
The ability to import types was released in v0.21 |
This is another in my series of threads about custom types. I can't find an original issue from which the custom types where originally imagined, so pardon the new threads on it, but I thought separate threads for each idea made more sense than one giant one.
Import Custom Types
In this first iteration of custom types, they're each defined in modules and provide a more rigid structure to the input parameters in a way that's consumable by the callers of these modulues. But I think this leaves ample opportunity on the table to better organize the data in larger "orchestration" modules upstream. Today, the root module remains a mess of raw data inputs that are just waiting to be passed into the structured types of the referenced modules.
I'd like to see greater reusability of types (like interfaces in C# or Typescript) and would thus to see the idea of namespaced types enter the larger picture. Bicep today treats separate files as whole modules and I'd like to build on that by allowing a wholly separate file to contain a collection of types. From a development perspective then, I'd like to see the inclusion of an
@import-types './types.bicep'
(naming is hard) statement at/near the top the file/module that can reference other modules and include their types from an intellisense and incorporation standpoint.Keep the approach today for those types that are module-specific as they don't need to be imported anywhere else. But this would allow for greater type re-use in a manner that also allows type usage outside of strictly from a parameter-validation scenario, e.g.:
Especially because the custom types themselves are bundled in with the deployment, the import of the types should not mean that all types are included in the modules importing them, but rather only those that are explicitly or implicitly referenced should be included in the output build.
Import Anything
Extending this idea a bit from just types, on a number of community calls I've heard asks for globalized values that can be referenced collectively across the deployment. I'd like to submit a variation of this as my own preferrerd solution to that ask. A true global namespace (like that in Javascript on the window) can be an unwieldy mess of variables (limiting the utility of Intellisense to narrow the intent) and really muddy the waters about what a (non-constant) variable is set to, especially given order-of-operations concerns.
Rather, I'd ask for a variation of the ask above - change the keyword from @import-types to @import and allow both types and variables to be passed as immutable constants back to the consuming modules. Locally, they can be manipulated and derivatives created as needed, but without being able to write back to the module they're originally defined in. Like the types, if the variables in the module aren't referenced, don't include them in the output build.
Because this module references the
assignments
variable from types.bicep, all types and variables from types.bicep would be included in this module at build.This module only uses two of the types (explicitly uses nodeTypes and implicitly uses nodeType) so it would exclude the other type and variable from the output module.
Import select types
Addressing some of the comments on this thread, it'd be great to be able to selectively import only some types. Just like the example immediately above this, it should emit identical output because both the
nodeType
(implicitly included via its explicit reference in the original type definition) andnodeTypes
type definition should be included in this outputted module as though it was defined here to begin with (assuming the same runtime as used today for custom types, but allowing room for optimization down the road), but the other unused types (certificateAssignment
) would be excluded because they're not explicitly imported. The earlier example excluded the remaining unused types implicitly because they were imported, but simply not used.Type aliases
Addressing the concern in #9639 around the symbolic names, it would be great to support aliases on the imported types to prevent in-module name collisions.
Repurposing the same example above, I'd like to offer the
as
keyword to denote the symbol to use for the type as an alias in place of its original symbol. When producing the output module (again, assuming today's ARM runtime approach for custom types wherein the type is bundled with every module), the alias should be used as the type's name instead of its original name so as to prevent any naming collisions. For example:When built, this should place the type definition inline using the alias such that it would be no different than having originally written (except that the former is easier on developers since it allows reusability and what follows is representative of the experience today):
These suggestions would greatly simplify the idea of using a deployment for a wide variety of implementation as these types and values could simply be implemented in project-wide and more narrowly-applicable modules can also be built as needed.
For locally-referenced imports, I can't immediately think of why the importation would be necessary to maintain in the build output and would instead recommend this be syntactical sugar for the repetition throughout the deployment. In other words, in the output ARM template, the variable and types should be referenced as though they were defined in the module and referencing modules themselves instead of imported.
Import Anything from Anywhere
Especially with the notion of public registries, I'd like to further see an evolution of the idea that allows for importing types and variables from versioned registry-based modules. I haven't worked much with the public registries myself, so I defer to the team as to whether it make sense to maintain a link to the registry for imports or again just use this import statement as a shortcut at build-time to copy all the utilized variables and types in the resulting module.
Change log:
2/13: Added suggestion to import specific types instead of a blanket import as well as aliases to imported symbols
Thank you for considering my request.
The text was updated successfully, but these errors were encountered: