Skip to content

Commit

Permalink
Add suport for typealias types (and never type) (#13)
Browse files Browse the repository at this point in the history
* Add suport for typealias types (and never type)

* add nothing type to primitive snippet test
  • Loading branch information
jasongwartz authored Mar 9, 2024
1 parent b569d0b commit 20ea5f1
Show file tree
Hide file tree
Showing 11 changed files with 125 additions and 8 deletions.
2 changes: 1 addition & 1 deletion codegen/snippet-tests/input/01-primitiveTypes.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ float: Float
bool: Boolean
nullType: Null
anyType: Any

nothingType: nothing
12 changes: 12 additions & 0 deletions codegen/snippet-tests/input/06-withTypeAlias.pkl
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
typealias MyStringAlias = String

x: MyStringAlias

class MyClassToBeAliased {
a: String
b: Int
}

typealias MyAliasedClass = MyClassToBeAliased

y: MyAliasedClass
2 changes: 2 additions & 0 deletions codegen/snippet-tests/output/01_primitive_types.pkl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ export interface N01PrimitiveTypes {
nullType: null

anyType: any

nothingType: never
}

// LoadFromPath loads the pkl module at the given path and evaluates it into a N01PrimitiveTypes
Expand Down
35 changes: 35 additions & 0 deletions codegen/snippet-tests/output/06_with_type_alias.pkl.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// This file was generated by `pkl-typescript` from Pkl module `06-withTypeAlias`.
// DO NOT EDIT.
import * as pklTypescript from "@pkl-community/pkl-typescript"

// Ref: Module root.
export interface N06WithTypeAlias {
x: MyStringAlias

y: MyAliasedClass
}

// Ref: Pkl class `06-withTypeAlias.MyClassToBeAliased`.
export interface MyClassToBeAliased {
a: string

b: number
}

type MyStringAlias = string

type MyAliasedClass = MyClassToBeAliased

// LoadFromPath loads the pkl module at the given path and evaluates it into a N06WithTypeAlias
export const loadFromPath = async (path: string): Promise<N06WithTypeAlias> => {
const evaluator = await pklTypescript.newEvaluator(pklTypescript.PreconfiguredOptions);
try {
const result = await load(evaluator, pklTypescript.FileSource(path));
return result
} finally {
evaluator.close()
}
};

export const load = (evaluator: pklTypescript.Evaluator, source: pklTypescript.ModuleSource): Promise<N06WithTypeAlias> =>
evaluator.evaluateModule(source) as Promise<N06WithTypeAlias>;
24 changes: 23 additions & 1 deletion codegen/src/Generator.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,33 @@ function gatherClasses(decl: List<reflect.TypeDeclaration>): Mixin<List<Typescri
seenMappings = accum
}))

function isEnumLike(decl: reflect.TypeDeclaration) =
decl is reflect.TypeAlias
&&
let (referent = decl.referent)
referent is reflect.UnionType
&& referent.members.every((t) -> t is EnumMember)
&&
if (referent.members is List<reflect.StringLiteralType>)
hasDistinctEnumNames(referent.members)
else true

function gatherTypeAliases(decl: List<reflect.TypeDeclaration>): Mixin<List<TypescriptMapping>> =
(acc) ->
decl
.filter((it) -> it is reflect.TypeAlias && !isEnumLike(it))
.fold(acc, (accum, it) -> accum.add(new TypescriptMapping.TypeAlias {
typescriptModule = getTypescriptModuleName(it)
source = it
seenMappings = accum
}))

local allMappings: List<TypescriptMapping> =
let (clazz = reflect.Module(moduleToGenerate).moduleClass)
let (declarations = gatherer.gatherTypeDeclarations(clazz, List()))
List() |>
gatherClasses(declarations)
gatherClasses(declarations) |>
gatherTypeAliases(declarations)

local modules = allMappings
.groupBy((it) -> it.typescriptModule)
Expand Down
2 changes: 2 additions & 0 deletions codegen/src/internal/Gen.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ mappings: List<TypescriptMapping>

/// The TypeScript contents
contents: String

typescriptModule: String?
13 changes: 13 additions & 0 deletions codegen/src/internal/TypeAliasGen.pkl
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
module pkl.typescript.internal.TypeAliasGen

extends "Gen.pkl"

import "typegen.pkl"
import "Type.pkl"
import "pkl:reflect"

typealiaz: reflect.TypeAlias = mapping.source as reflect.TypeAlias

type: Type = typegen.generateType(typealiaz.referent, typealiaz, mappings)

contents = "type \(mapping.name) = \(type.render(typescriptModule))"
15 changes: 15 additions & 0 deletions codegen/src/internal/TypescriptMapping.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,21 @@ class Enum extends TypescriptMapping {
)
}

class TypeAlias extends TypescriptMapping {
local self = this

alias: reflect.TypeAlias = self.source as reflect.TypeAlias

name = utils.toTypescriptName(self.source)

names = List(name)

type = new Type.Declared {
typeName = name
`module` = self.typescriptModule
}
}

class Class extends TypescriptMapping {
local self = this

Expand Down
18 changes: 12 additions & 6 deletions codegen/src/internal/TypescriptModule.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import "pkl:reflect"
import "TypescriptMapping.pkl"
import "Gen.pkl"
import "ClassGen.pkl"
import "TypeAliasGen.pkl"
import "utils.pkl"

`module`: reflect.Module
Expand Down Expand Up @@ -48,12 +49,17 @@ local function hasUniqueNames(): Boolean =

local generated: List<Gen>(hasUniqueNames()) =
moduleMappings.map((it) ->
// if (it is TypescriptMapping.Class)
new ClassGen {
mappings = module.mappings
mapping = it
})
// }
if (it is TypescriptMapping.TypeAlias)
new TypeAliasGen {
mappings = module.mappings
mapping = it
}
else
new ClassGen {
mappings = module.mappings
mapping = it
}
)
// If not a class, what then?

contents: String = new Listing {
Expand Down
1 change: 1 addition & 0 deletions codegen/src/internal/typegen.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ mappedTypes: Mapping<Class|TypeAlias, Type> = new {
[Boolean] = new Type.Declared { typeName = "boolean" }
[Null] = new Type.Declared { typeName = "null" }
[Any] = anyType
[Char] = new Type.Declared { typeName = "string" }
[Duration] = new Type.Declared {
`module` = "pkl-types"
typeName = "Duration"
Expand Down
9 changes: 9 additions & 0 deletions codegen/src/tests/typegen.test.pkl
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,12 @@ local class Pairs {

local reflectedPairs = reflect.Class(Pairs)

local class Nothing {
res1: nothing
}

local nothingType: reflect.Type = reflect.Class(Nothing).properties["res1"].type

local mod = reflect.Module(module).moduleClass

local function generateType(typ: reflect.Type) = typegen.generateType(typ, mod, List()).render("")
Expand All @@ -67,6 +73,9 @@ facts {
generateType(reflect.dynamicType) == "PklTypes.Dynamic"
generateType(reflect.dataSizeType) == "PklTypes.DataSize"
generateType(reflect.durationType) == "PklTypes.Duration"
generateType(nothingType) == "never"
generateType(reflect.DeclaredType(reflect.TypeAlias(Char))) == "string"
generateType(reflect.DeclaredType(reflect.Class(Null))) == "null"
}
["maps"] {
generateType(reflectedMaps.properties["res1"].type) == "Map<string, string>"
Expand Down

0 comments on commit 20ea5f1

Please sign in to comment.