Skip to content

Instantly share code, notes, and snippets.

@garrow
Created March 17, 2022 23:34
Show Gist options
  • Save garrow/f9982c36662ce8eaf88612a2b229aa88 to your computer and use it in GitHub Desktop.
Save garrow/f9982c36662ce8eaf88612a2b229aa88 to your computer and use it in GitHub Desktop.
An attempt to make the Typescript compiler do some basic validation of a petri net, ensuring that only defined places (nodes) can be used, both for the places, and transitions (vertices) of the graph.
export {}
// Representing Petri Nets as typed values in Typescript.
type PlaceID = string
type Place<P extends PlaceID = PlaceID> = {
id: P
}
type Transition<P> = {
start: P
end: P
}
type PetriNet<P extends PlaceID = PlaceID> = {
places: Set<Place<P>>
transitions: Set<Transition<P>>
}
// This is the first magic:
// See: https://stackoverflow.com/questions/60496276/typescript-derive-union-type-from-array-of-objects
// type Allowed = typeof knownPlaces[number]['id']
function registerPlaces<T extends readonly Place[] & Array<{ id: V }>, V extends string>(...args: T): T {
return args
}
// Typescript then can ensure only defined placeIDs can be used in transitions and places
function petriNet<Places extends PlaceID>(
places: readonly Place<Places>[],
transitions: Transition<Places>[]
): PetriNet<Places> {
return {
places: new Set(places),
transitions: new Set(transitions)
}
}
// ---------------------------------------------------------------------------------------------------------------
const alphaPlaces = registerPlaces(
{id: 'a'},
{id: 'b'},
{id: 'z'}
)
const alphaNet = petriNet<typeof alphaPlaces[number]['id']>(alphaPlaces, [
{start: 'a', end: 'b'},
{start: 'a', end: 'z'}
])
console.log('alphaNet')
console.log(alphaNet)
// -- Bad Transitions
const badNet = petriNet<typeof alphaPlaces[number]['id']>(alphaPlaces, [
{start: 'a', end: 'b'},
// TS2322: Type '"c"' is not assignable to type '"a" | "b" | "z"'.
{start: 'a', end: 'c'}
])
console.log('badNet')
console.log(badNet)
// ---------------------------------------------------------------------------------------------------------------
// -- Bad Places
const otherPlaces = registerPlaces(
{id: 'b'},
{id: 'c'}
)
// TS2345: Argument of type '[{ id: "b"; }, { id: "c"; }]' is not assignable to parameter of type 'readonly Place<"a" | "b" | "z">[]'.
// Type '"c"' is not assignable to type '"a" | "b" | "z"'.
const badPlaces = petriNet<typeof alphaPlaces[number]['id']>(otherPlaces, [
{start: 'c', end: 'b'},
{start: 'b', end: 'c'}
]
)
console.log(badPlaces)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment