Change language

TypeScript Berlin Meetup #8 – Robert Wolff – Dealing with untyped runtime libraries in TypeScript

TypeScript Berlin Meetup #8 - Robert Wolff - Dealing with untyped runtime libraries in TypeScript

(bright music) - Before I tell you who I am and what I do, I realized in the beginning that most of you are actually way more senior than I am.

So, we need to play a little game.

I need you to imagine that this is you.

You are an awesome team that just recently discovered TypeScript, maybe like two years ago, you abandoned your JavaScript code and you moved everything over to TypeScript.

And it also so happens that you are working for an E-commerce company and that you work for the Payments team.

And now the product owner asks you to implement a new payment method.

And for that, you have to reach out to a partner.

And this partner, coincidentally is Klarna, and they offer an SDK that you should integrate into your code.

So you go to their webpage, you go through their documentation, and then you slowly and carefully read this and then it hits you, this is a JavaScript runtime SDK.

(audience laughing) So youre taking a deep breath and just say to yourself, this is not a problem.

Runtime SDKs, they are inherently a little bit difficult because while youre writing your code, the compiler cannot do anything.

But theres this awesome page called DefinitelyTyped and Klarna is one of the hottest startups in Europe, so surely they publish their types.

So you search for Klarna and then, their search not coming up.

But then you search again and you scour the internet, and then, obviously, there are very good developers that publish their own types for Klarna.

So you try them out and then you find out that those are maybe not so production ready that you want to use this in your E-commerce platform, or maybe some of them are a little bit outdated.

And if this has ever happened to you, then welcome to my world.

My name is Robert.

Im working in the Payment team for Idealo.

And what Im about to show you is nothing fancy but may be helpful if you onboard new developers to your team and you have to deal with runtime libraries.

Why is this even relevant? So we all know what to do.

We just cast it to any, and then we can, yeah, because its about developer experience.

And I actually, I got the inspiration for this talk from Anil.

Is he actually here tonight? He is not.

Who was talking about TypeScript pitfalls and a great developer experience.

So I wanted to go into that and I wanna do some live coding because this is always fun when in the meetup.

So unfortunately, I cannot show you any Idealo code.

So I had to quickly write my own E-commerce platform, which I will demo now here.

Of course, we have the resemblance to create React App is totally coincidental.

So dont worry about that.

I dont want to print.

I want to open a file.

What does this App do? So this is a React App.

I know theres some React developers here, so its not totally foreign.

It just has some state.

It has a success function that sets some stuff and this success function is passed to an invoice component.

Now, this invoice component, if you just look at the return statement here, it has this wonderful paragraph, click the link below to pay with invoice.

And the test is wonderful button here.

And then this thing in the middle, thats the weird thing.

Thats where we load the Klarna SDK.

And this is coming from a hook.

This hook receives the ID of the element and it receives the success function from the parent app.

And it returns a ready state and a checkout function that we are using on all button.

How does the hook work? Well, this is where we get into our third party library.

So, we already know we need to add Klarna to window, and then we need to do some stuff with it.

And the first thing we need to do is call Payments INIT on windows.

So lets actually double check that window.

Why does it always ask me to print? Brave, Jesus.

Lets double check that this actually exists.

So this might be a little bit tiny.

Im sorry but I just wanna just see that yes, Klarna is here, and if I say Payments, theres a bunch of functions.

You cant read it, it doesnt matter.

It just proves that the functions are there.

They are on the window object.

So we need to pass a client token to this function.

And the client token, thats just the decide we get from a server because when youre working with Payments, you dont wanna really make requests from your client to your payment service provider.

You should always use the server for that.

If youre not sure why, you can approach me after the talk.

And then we try to use Klarna on window.

And here we have our first problem, obviously.

If I just do this, then the compiler will complain because window is an object.

It has the definitions, but Klarna, we just edited here, the compiler doesnt know what to do.

So we need to cast it to any.

And then we have all sorts of problems because now what do we get? Well, first of all, we dont get any auto-complete here.

And we can also, if we mess up, so right now, this should still be working, but if I accidentally do this, then obviously, it breaks, and the page doesnt necessarily tell me why it breaks.

And here in my code, I may notice the tone is not a word that exists, but anyway, and distracts on and on and on.

So here, Im using the load function on Payments.

I need to pass it some callback.

I dont know what the callback returns, maybe I get from the documentation that I need some sort of a show form bullion that I need to use.

And then eventually, if I finally want to click this, buy now, button, and theres a checkout function that calls authorize on the library, I have to supply my birth date here, and then it cords the authorized end point on the server, I get an order ID back.

And if I click on the order ID, then it opens the Idealo drop page because were hiring.

And this is all great, but were basically not using TypeScript here.

So what can we do? Well, like Anil taught us, we can use unknown instead of any, because unknown forces you to make certain assertions.

So lets actually try that.

I will quickly hack together, AKA check out a different version where we do just that.

So we start with unknown, and we then have to do a couple of work, a little bit of work.

We have to check that windows actually an objectives, not now.

We then actually have to see that Klarna is a property of window.

And then we also have to check that Klarna is an object, and then sure enough, the compiler lets us use Klarna, yeah, and we kinda sign it.

Then we just do the same with Payments, and then we just do the same with INIT.

And you can already see the problem.

This works, but is it really a great developer experience? So the senior guys, a few, they probably already see that this can be reflected in the fancy recursive function, but I didnt have the time to do that.

Also I had to copy some code from not Stack Overflow but from some other site to use this has on property thing, because otherwise there would be an, if statement, and its kinda ugly.

So there must be a better way.

So how about we do this? So in this version, ignore the stuff below.

In this version, we can actually access Klarna directly on the window.

And the IDE actually suggests that we can use this.

And why? Well, we are already used some of our own types, and those types are coming from a file that where we have our own module declaration, own global namespace, we have our own types, theyre very simple.

And this is kinda need, I also learned this in the last meetup.

We just add Klarna to the window interface.

So why does this work? This works because theres this thing called declaration merging, where you can merge namespaces or interfaces that have the same name into a single definition.

And this comes super handy.

It might also get very confusing if youre not aware that this is happening because certainly you are adding stuff to the window or to other existing interfaces that other developers might not expect.

However, it allows you to actually turn this mess of anys and unknowns into something very pleasant in very few lines of code.

So what we have here now is we have our interface with Klarna, which gets a Klarna type attached, and theres a Payments type, and the Payments type just has an INIT, a load, and an authorize function, just like we grabbed it from the documentation over here.

And for example, the load function we see clearly, theres a configuration.

This configuration takes a string and a category which is one of those two, and a load callback, and the load callback is also typed.

And this one also has a response, and the response has a show form bullion.

And with this, if we go back over to how the solution now it looks like, you will realize that there is normal costs and normal assertions and normal checks.

And the best of all, if I now basically just take this away and I ask my former colleague, Hey, please, can you do some stuff with Klarna? We need this hook or we need this function, we need this effect.

You need to initialize it.

Now we can just do, he can just go in and see, aha, Klarna.

Okay, theres theres Payments there.

And whats on Payments? Authorized in it load.

He told me to INIT.

Okay, then let me actually INIT.

What does it take? Aha, it takes some configurations, so lets actually check whats on there.

Theres a client talking.

I remember I used the client talking before.

And just like that, you dont even have to go to the documentation to be able to complete this.

And the best part is this doesnt even compile anymore.

So you get the best of all.

Obviously, its not 100% safe because if you mess the initial part up here where you add Klarna to the window, then the compiler basically doesnt know about that.

If anybody in the audience has an idea how to solve that, I would be super interested in knowing that.

But theres that.

We have everything here.

We can even restructure our arguments that get passed into the callback, and developers can have a super easy time.

Also as an additional benefit, if we look at tests.

So here we have to, I mean, this test for the obviously was already there from the beginning because it will test driven development.

For tests, its also helpful because yes, testing React hooks is a bit cumbersome, but even here, you can use the type system to make sure that the library is used correctly or even better.

Because when youre writing your hook, you dont want to test the Klarna library.

Thats Klarnas job.

What you want to test is your code.

You want to basically test if you use this hook with a certain, with the supply parameters and the Klarnas script loads, then you want the Klarna scripts ready to be true when it is loaded.

And if we just go over here and run this, and the testing demo gods are kind to me, then these tests hopefully should pass, nice.

So yeah, do your fellow developers and especially junior developers like me a favor.

And whenever you work with untyped third party libraries and you dont find the definitions for that, just add them.

Its like 15 minutes of work.

Okay, or maybe three hours, but it will save you so much time (all laughing) that is invaluable.

And with that, thank you very much for listening.

And Im curious if you have questions.

(audience clapping) - [Man] When are you gonna upload the Klarna types-- - I knew that this question was coming.

(all laughing) So the thing is, I said this is 50 lines of code.

But this is, if I go back to the console here and I try to make this as big as possible.

Can I actually make this real big? Lets do work here in brave.

- [Man] Yeah, it should be (mumbles) - Yeah, it does, nice.

So if we just take a look here, is we use Payments and we use three functions within Payments, theres credit, theres direct bank transfer, theres direct debit.

So theres a lot more on the root level.

Then if you look at Payments, theres also quite a lot.

And we basically, the reason why were not uploading it is because were using 5% of the library.

And as long as we dont have to use more, it will be kinda shitty to upload an incomplete type definition, because then it just ends up like those other projects that are either not production ready or deprecated.

And thats we kinda want to avoid.

I just hope that Klarna, at some point, does upload the definitions.

That would be awesome.

- [Man] Might be a hard question, but have you faced the problem where they just updated the packet and then theyre like, oh? - Very good question.

So (inaudible) (Robert laughing) I anticipated that one and Im actually waiting for the day that Klarna does that.

But interestingly enough, they have so many clients and they are so big.

This source where we get the SDK from has not changed in the last four years.

And the API also has not changed, or it has not been there.

We are not breaking changes in the last four years to keep adding to it.

Obviously, theyre adding new functionality, but so far there was no problem with that.

I will say though, their Sandbox environment sometimes goes down.

It was down this afternoon and I was a little bit nervous.

Luckily, it get came back up for the talk.

Yeah, but yeah, there is a concern.

If they ever update this without telling their customers, they will be in trouble.

- [Man] Thats what I was wondering, okay.

- Is there another question? - [Man] Yeah, yeah, maybe an answer.

So you asked like, well, how you connect your Nexus, like this (mumbles) that you have, so you have to do some steps to perform before you can actually use them.

So if you mess up there, then maybe the two types dont have, I see the essence is kind of type state pattern where you basically produce types where this information is kind of encoded.

So its not trivial but its definitely doable.

So youre saying that I only get this type when this (mumbles) initialized.

I guess like, phantom types of information where you indicate that the compiler, or use a mitigation to the compiler that you cannot use this property if this function has not been called before, maybe could help you in this case.

- Awesome, I will come back to you on that.

(all laughing) All right, then I guess its time for the quiz.

- [Woman] Whenever youre present, Robert.

- You will find the quiz question maybe not as hard as the previous one, so (all laughing) - [Woman] Thank you.

(bright music)