Created
March 17, 2022 23:34
-
-
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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