Open rubythulhu opened 7 months ago
We can discuss it more on slack, but the first example does successfully codegen for me, working with a pkl-gen-typescript
from latest main
branch, built using npm run build
and executed via ./dist/bin/pkl-gen-typescript
. Here's the output:
// This file was generated by `pkl-typescript` from Pkl module `infuse`.
// DO NOT EDIT.
import * as pklTypescript from "@pkl-community/pkl-typescript"
// Ref: Module root.
export interface Infuse {
standardInfuseRule: pklTypescript.Any
damageInfuseRule: pklTypescript.Any
}
// Ref: Pkl class `infuse.StatMultiplier`.
export interface StatMultiplier {
type: pklTypescript.Any
num: number
}
// Ref: Pkl class `infuse.StatReplacer`.
export interface StatReplacer {
type: pklTypescript.Any
num: number
}
// Ref: Pkl class `infuse.StatAdder`.
export interface StatAdder {
type: pklTypescript.Any
num: number
}
// Ref: Pkl class `infuse.TransformInfuseRule`.
export interface TransformInfuseRule {
type: pklTypescript.Any
into: string
}
// Ref: Pkl class `infuse.BasicInfuseRule`.
export interface BasicInfuseRule {
type: pklTypescript.Any
supply: InfuseStat|null
hp: InfuseStat|null
damage: InfuseStat|null
reloadTime: InfuseStat|null
cooldown: InfuseStat|null
}
// Ref: Pkl type `infuse.InfuseStat`.
type InfuseStat = StatMultiplier | StatReplacer | StatAdder
// Ref: Pkl type `infuse.InfuseRule`.
type InfuseRule = TransformInfuseRule | BasicInfuseRule
// LoadFromPath loads the pkl module at the given path and evaluates it into a Infuse
export const loadFromPath = async (path: string): Promise<Infuse> => {
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<Infuse> =>
evaluator.evaluateModule(source) as Promise<Infuse>;
Separately from the bug report, some notes on your Pkl code:
fixed type = "transform"
are coming out of codegen as type: pklTypescript.Any
. That's because there is a semantic difference in Pkl between type = "transform"
and type: String = "transform"
- the former is actually of Any type with a value set to the string literal.type: "transform"
(ie. a string literal type) as opposed to a fixed
property. That also has the benefit of producing generated typescript that also is defined as a string literal typeclass StatMultiplier { fixed type="mult"; num: Number }
, you're using type
as a sort of discriminated union - where you can use type
to figure out the class type. Again, you're limited by the variables being typed as Any
. If you use literal types, and get a TypeScript string literal type in the output, you can write TypeScript code that does stuff like if (type === "mult")
which has nice benefits: a) the string you're checking will be autocompleted in your editor, and b) TypeScript will do "type narrowing" based on the comparison you do. I just added an example of this to the tests - in that example, if I had a variable myFruit
of type Fruit
, and did a if (myFruit.type === "apple") { ... }
, TypeScript would allow me to safely access myFruit.sweetness
inside the conditional blockSeparately from the bug report, some notes on your Pkl code:
- You'll notice that properties defined like
fixed type = "transform"
are coming out of codegen astype: pklTypescript.Any
. That's because there is a semantic difference in Pkl betweentype = "transform"
andtype: String = "transform"
- the former is actually of Any type with a value set to the string literal.- In cases like these, where you want it to be a specific literal value, you might get better results using
type: "transform"
(ie. a string literal type) as opposed to afixed
property. That also has the benefit of producing generated typescript that also is defined as a string literal type- it looks like with your classes up top, like
class StatMultiplier { fixed type="mult"; num: Number }
, you're usingtype
as a sort of discriminated union - where you can usetype
to figure out the class type. Again, you're limited by the variables being typed asAny
. If you use literal types, and get a TypeScript string literal type in the output, you can write TypeScript code that does stuff likeif (type === "mult")
which has nice benefits: a) the string you're checking will be autocompleted in your editor, and b) TypeScript will do "type narrowing" based on the comparison you do. I just added an example of this to the tests - in that example, if I had a variablemyFruit
of typeFruit
, and did aif (myFruit.type === "apple") { ... }
, TypeScript would allow me to safely accessmyFruit.sweetness
inside the conditional block
I've been so confused about the Anys, thank you! I'll clean those up as that was exactly the effect i was looking for, i just didn't realize that was the difference between fixed foo = "blah"
and foo: "blah"
, and i did assume that the foo =
form used an inferred type and not just Any, too used to type inference being all over the place nowadays.
I get the following (confusing!) error. Code is below:
This causes the error:
This works:
The interesting bit is there's no problem with
InfuseStat
, justInfuseRule
, and the only major diff i can see is that the types inside InfuseStat all have the same shape, but the rules have different keys...