@pkl-community/pkl-typescript v0.0.16
THIS LIBRARY IS CURRENTLY PRE-RELEASE
pkl-typescript
is currently major versionv0
, and breaking changes will happen between versions.Please read the section Roadmap below to learn more.
Pkl Bindings for TypeScript
This library exposes TypeScript language bindings for the Pkl configuration language.
These language bindings are made up of:
- an "evaluator", that can execute Pkl code and deserialise the result into JavaScript runtime objects
- the
pkl-gen-typescript
CLI, that can analyse a Pkl schema and generate matching TypeScript definitions
Together, this allows you to embed Pkl into your TypeScript application, complete with code generation for full type safety and ease of use.
Getting Started
First, install pkl-typescript
from NPM:
npm install @pkl-community/pkl-typescript
Then, generate a TypeScript file from your Pkl schema (eg. for a file called "config.pkl"):
npx pkl-gen-typescript config.pkl -o ./generated
Lastly, in your TypeScript code, you can import the generated types and loader code:
import { type Config, loadFromPath } from "./generated/config.pkl.ts";
const config: Config = await loadFromPath("config.pkl");
See more example usage in the examples directory.
Note on Schemas vs. Configs
pkl-gen-typescript
generates a TypeScript file based on Pkl's type information only, not Pkl's runtime values. For example, a Pkl file with x: String = "hello"
would produce the TypeScript type x: string
.
Conversely, the evaluator (used by the loadFromPath(string)
function) evaluates a Pkl module that renders values.
You may choose to have your Pkl schemas and values defined in separate Pkl files (eg. schema.pkl
and config.pkl
, where config.pkl
starts with amends "schema.pkl"
). In such a case, you would pass schema.pkl
to pkl-gen-typescript
, but then evaluate config.pkl
at runtime (ie. await loadFromPath("config.pkl")
).
Roadmap
This library is currently in pre-release: we believe it is usable and productive in its current state, but not feature-complete, and not yet API-stable.
We will keep the major version at v0
until we are ready to commit to stability in:
- the evaluator API (as provided by the
@pkl-community/pkl-typescript
NPM package) - the TypeScript type definitions generated by
pkl-gen-typescript
Until then, minor and patch releases may contain breaking changes.
!WARNING
We strongly recommend you regenerate your generated TypeScript code (withpkl-gen-typescript
) every time you upgrade@pkl-community/pkl-typescript
. If you don't, you may end up with unexpected runtime errors from type mismatches.
Known Current Limitations
- Inlined imports: Imported Pkl types are inlined into the output TypeScript file. For example, if
foo.pkl
has an import likeimport "bar.pkl"
, and you runpkl-gen-typescript foo.pkl
, the resultingfoo.pkl.ts
file will include all types defined infoo.pkl
as well as all types defined inbar.pkl
. This means that the resulting TypeScript generated files (in a multi-file codegen) will match the set of input root files, not the file structure of the source Pkl files. This behaviour may create unintended name conflicts; these can be resolved using the@typescript.Name { value = "..." }
annotation. It may also cause duplication (eg. if the same shared Pkl library file is imported in two schemas); TypeScript's structural typing (where equivalent type shapes can be used interchangeably) should mean that any duplicate types can be safely used as each other. - Subclass type overrides: Pkl class definitions are generated as TypeScript interfaces in code generation; Pkl supports completely changing the type of a property in a child class, but this is not allowed in TypeScript extending interfaces. When a TypeScript interface
extends
a parent interface, overrides of the type of a property must be "compatible" with the parent type (eg. overriding astring
type with a string-literal type). TypeScript codegen currently has support for a few compatible types, and others may be allowed in the future (if you have an example of a compatible type that should work but fails in codegen, please file a GitHub Issue). - Regex deserialisation: Pkl's
Regex
type will be decoded as apklTypescript.Regex
object, which contains a.pattern
property. Pkl uses Java's regular expression syntax, which may not always be perfectly compatible with JavaScript's regular expression syntax. If you want to use your PklRegex
as a JavaScriptRegExp
, and you are confident that the expression will behave the same way in JavaScript as in Pkl, you can instantiate a newRegExp
using thepklTypescript.Regex.pattern
property, eg.const myConfigRegexp = new RegExp(myConfig.someRegex.pattern)
. - IntSeq deserialisation: Pkl's
IntSeq
type is intended to be used internally within a Pkl program to create a range loop. It is unlikely to be useful as a property type in JavaScript, and is therefore decoded into a custompklTypescript.IntSeq
type with signature{ start: number; end: number: step: number }
- it is not decoded into an array containing the ranged values. If you have a use-case to useIntSeq
as an array of ranged values in a TypeScript program, please file a GitHub Issue. - Duration and DataSize APIs: Pkl has a rich API for many of its custom types, but two of note (that are not common in standard libraries of other languages) are
Duration
andDataSize
, which include convenience APIs for eg. converting between units or summing values. These types are decoded intopklTypescript.DataSize
/pklTypescript.Duration
types (each of which have avalue
andunit
property), and do not yet have the convenience APIs from Pkl.
Appendix
Pkl Binary Version
This package has a peer dependency on @pkl-community/pkl
, to ensure a Pkl binary is installed. You can use an alternative Pkl binary (for either the evaluator or codegen) by setting the environment variable PKL_EXEC
with the path to a Pkl binary.
Type Mappings
When code-generating TypeScript type definitions from Pkl schemas, each Pkl type is converted to an associated TypeScript type, as per the table below. While in pre-release, these mappings are subject to change!
Pkl type | TypeScript type |
---|---|
Null | null |
Boolean | boolean |
String | string |
Int | number |
Int8 | number |
Int16 | number |
Int32 | number |
UInt | number |
UInt8 | number |
UInt16 | number |
UInt32 | number |
Float | number |
Number | number |
List | Array<T> |
Listing | Array<T> |
Map<K, V> | Map<K, V> |
Mapping<K, V> | Map<K, V> |
Set | Set<T> |
Pair<A, B> | pklTypescript.Pair<A, B> |
Dynamic | pklTypescript.Dynamic |
DataSize | pklTypescript.DataSize |
Duration | pklTypescript.Duration |
IntSeq | pklTypescript.IntSeq |
Class | interface |
TypeAlias | typealias |
Any | pklTypescript.Any |
Unions (A|B|C) | A\|B\|C |
Regex | pklTypescript.Regex |