Typescript proxy magic
2021-05-13
As part of my work I published a blog post on how we are able to build a Typescript-based abstraction layer on top of the Azure Data Factory (ADF) API. Given that the "Typescript magic" code was my personal contribution, I wanted to do a quick personal post to show how a specific part of it works.
The relevant piece is near the end where we are able to replace a dynamic language run by ADF (normally written as plain text) with Typescript code that "compiles" to the correct text while providing type safety:
// This string is how we would normally write a dynamic property:
`@concat(pipeline().paramaters.Folder, "/", pipeline().paramaters.File)`;
// This is the updated Typescript method:
.parameters.Folder,
"/",
.parameters.File
;
The specific part I wanted to dig into is the pipeline<T>()
function. This is the code we used:
;
;
The createParamProxy
function is the core part of this. Basically, it (ab)uses the JS Proxy API to create a dynamic object that does the following:
- When you access a property on the paramProxy, it will return a new paramProxy with the name of the parent path added to the new property name.
- Unless you call
toJSON
ortoString
, in which case it will dump the entire path out as a string.
The clearest examples of this are the test cases for the createParamProxy
function:
"createParamProxy with parent toString()",;
"createParamProxy without parent toString()",;
"createParamProxy nesting",;
And for an example of it in action with the pipeline
function, we can use this test case:
"pipeline syntax with other functions",;
All in all, this was a pretty cool example of how Typescript and Javascript operate at two different layers and that you can deliberately decouple those layers to do interesting things. Stay tuned for another post where I'll dig into how we used Typescript types to ensure functions like concat
(which "compile" to the string "concat()"
) can be made type safe to ensure it only accepts other strings as inputs (i.e. concat("a string", 2)
fails to compile as Typescript).