Skip to content

Instantly share code, notes, and snippets.

@jeskew
Last active April 19, 2023 16:14
Show Gist options
  • Save jeskew/fc075f7a163fe021b42ca2c190d5383c to your computer and use it in GitHub Desktop.
Save jeskew/fc075f7a163fe021b42ca2c190d5383c to your computer and use it in GitHub Desktop.
Overview of the user-defined types preview feature

This content has been migrated to https://learn.microsoft.com/en-us/azure/azure-resource-manager/bicep/user-defined-data-types

User-defined types in Bicep

Bicep 0.12 includes a preview of enhanced parameter type checking. You can enable the preview in your project's bicepconfig.json file:

{
    "experimentalFeaturesEnabled": {
        "userDefinedTypes": true
    }
}

This feature introduces a new type expression syntax that allows Bicep users to constrain the structure of object and array parameters passed into a template and to create reusable type symbols.

What is a type expression?

Valid type expressions include:

Symbolic references

Symbolic references are identifiers that may refer to an "ambient" type (like string or int) or a user-defined type symbol declared in a type statement:

// ambient type reference
type myString = string

// user-defined type reference
type myOtherString = myString

Array types

Array types may be declared by suffixing [] to any valid type expression.

param strings string[] = []
param arrayOfArraysOfInts int[][]

Object types

Object types contain zero or more properties between curly brackets:

type myObject = {
  stringProp: string
  recursiveProp?: myObject
}

Properties must have a name on the left-hand side of the colon and a value on the right-hand side. The name may be any string (values that would not be a valid identifier must be enclosed in quotes), and the value may be any type syntax expression. Properties are required unless they have an optionality marker (?) between the property name and the colon.

Recursion

Object types may use direct or indirect recursion so long as at least leg of the path to the recursion point is optional. For example, the myObject definition above is valid (because the directly recursive recursiveProp property is optional), but the following would not be:

type invalidRecursiveObject = {
  level1: {
    level2: {
      level3: {
        level4: {
          level5: invalidRecursiveObject
        }
      }
    }
  }
}

Because none of level1, level2, level3, level4, or level5 is optional, there is no JSON object that would be able to fulfill this schema.

Primitive literals

Strings, integers, and booleans are all valid types.

type stringLiteral = 'string'
type intLiteral = 10
type boolLiteral = true

Unary operation on literal-typed type expressions

Unary operators may be used with integer and boolean literals or references to integer or boolean literal-typed symbols:

type negativeIntLiteral = -10
type negatedIntReference = -negativeIntLiteral

type negatedBoolLiteral = !true
type negatedBoolReference = !negatedBoolLiteral

Unions

Unions may include any number of literal-typed expressions. Union types are translated into the allowedValues constraint in ARM, so only literals are permitted as members.

param oneOfSeveralObjects {foo: 'bar'} | {fizz: 'buzz'} | {snap: 'crackle'}
param mixedTypeArray ('fizz' | 42 | {an: 'object'} | null)[]

Where can type expressions be used?

Type expressions can be used in four places:

  1. As the type clause of a param statement (i.e., where previously you would specify string, int, etc.),
  2. Following the = in a type statement,
  3. Following the : in an object type property, or
  4. Preceding the [] in an array type expression.

Putting it all together

autocomplete_demo

What's next?

The user-defined types feature is still under active development, and there are several features that are planned but not yet ready for use.

Tuple types

Like TypeScript tuples, this type would allow users to specify an array of a known, fixed length where each index has its own schema.

type aTuple = [int, string, bool]

Type constraints on outputs

Each of the type expressions outlined above is expressed in ARM JSON templates as validation constraints. For example,

param objectParam {
  stringProp: string
}

is expressed in ARM as

{
  "parameters": {
    "objectParam": {
      "type": "object",
      "required": ["stringProp"],
      "properties": {
        "stringProp": {
          "type": "string"
        }
      }
    }
  }
}

ARM does not currently enforce validation constraints on outputs, though this is something we plan to enable.

A typeof keyword or function

When dealing with Azure service types, you shouldn't need to redeclare the types a service has already defined. A statement like the following should be permitted in a future release:

param keyVaultNetworkAcls typeof('Microsoft.KeyVault/vaults@2022-07-01').properties.networkAcls
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment