title: Basics of Typesscript slug: typescript-basics
Introduction
Why
Typescript makes it harder to write JavaScript incorrectly.
What
Typescript is a dailect of JavaScript that adds new syntax to define types and interfaces for variables, function arguments, function return types and objects. The typescript compiler to
Quick Start
To get started with Typescript without worrying about setup, you can create a new project using tsdx:
npx tsdx create learn-typescript
Also, adding typescript to create-react-app is documented and straight-forward.
I wrote a longer introduction for Pantheon.
Benfit For React Code
Imagine this component to show a WordPress post:
const Post = ({ title, content, author }) => (
<div>
<PostTitle title={title} />
<PostContent content={content} />
<PostAuthor author={author} />
</div>
);
This component assumess that its props match the expected props of the child components. We can test that's the case with tests that have the correct props:
describe("Post", () => {
it("Puts the title in the right place", () => {
//Shallow is enzyme's shallow renderer
const post = {
title : {
rendered: 'Hello Saturn'
};
//...
};
//render with sample data
const wrapper = shallow(
<Post {...post} />
);
//Make sure prop is passed correctly
expect(wrapper.find(PostTitle).prop("title")).toBe(post.title.rendered);
});
});
A good thing about this test is that if there are run time erros causesd by running this component, the tests will show you those erorrs. This test is an example of what I condsider an anti-pattern -- testing React's functionality, not the actual code being created.
This type of rigid testing makes refactoring safe, and gives us assurances our props are mapped correctly. But, it's also a a lot of boring test code. This kind of test likely will need to get rewritten as the componets are refactored, and that's a bad smell.
The problem this kind of testing solves is thatwe could have run-time errors if the componets and sub-components being tested are not used according to what we assume there public APIs, errors will happen. We can assume that if we run the components in a production-like environment, and errors happen, that corrisponds to what an end user would describe as "broken" in a product environment.
OK, fine, but two questions:
- Why are we assuming the public API of the components?
- Why do we have to wait until the program is run to see its errors?
If we are implimenting the component to show the title of a WordPress post, it might look like this, assuming title is a string, not an object, as its returned from the WordPress REST API:
const PostTitle = ({ title }) => <h1>{title}</h1>;
Just looking at this code, I can see it has a bug and will generate an error when it runs:
const post = {
title : {
rendered: 'Hello Saturn'
};
//...
};
const PostTitle = ({ title }) => <h1>{title}</h1>;
const Post = ({post})=> <div><PostTitle title={post.title} /></div>;
I can see this will generate an error, beacuse I can infer how the code is supposed to work by running it. Right now, to get my computer to show that error, I have to run the code, which seems silly, a computer should be able to know how it is supposed to be used.
This is why we have static analysis. Before we compile our JSX to JavaScript or compile any JavaScript from the dialect we are writing in to one that runs in the browser, or our server, we can opt into analyzing the code for likely erros caused by passing the wrong types to functions -- for example the props passed to a React component.
const post = {
title : {
rendered: 'Hello Saturn'
};
//...
};
const PostTitle = (props: { title:string }) => <h1>{title}</h1>;
const Post = (props: {title: {rendered:string}})=> (
<div>
<PostTitle title={post.title} />
</div>
);
Now, this is as obviously wrong to the Typescript compiler as it is to me and requires refactoring to:
const PostTitle = (props: { title: string }) => <h1>{title}</h1>;
const Post = (props: { title: { rendered: string } }) => (
<div>
<PostTitle title={post.title.rendered} />
</div>
);
Now, that I'm not assuming the public API of my components, the compiler and my IDE can tell me in advance if I am using the components correctly or now.
Or maybe this component will take the whole post object, not just the string, and handle getting to the right part of the title object:
const PostTitle = ({ title }) => <h1>{title.rendered}</h1>;
Or maybe we want the whole post, so we can use it's ID and post type in the markup:
const PostTitle = ({ post }) => (
<h1 className={post.type} id={post.id}>
{post.title.rendered}
</h1>
);
Most likely I'm going to go through several implimentations before I find something that works. For each change, I need to know that my props are mapped correctly. Instead of rewriting my tests, or writing tests outside of my unique business logic, I'm going to assume that if my components are used correctly, that React will run them correctly.