arrow-left

All pages
gitbookPowered by GitBook
1 of 7

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Loading...

Typescript Rules:

Typescript Types

hashtag
Everyday Types

In this chapter, we'll cover some of the most common types of values you'll find in JavaScript code, and explain the corresponding ways to describe those types in TypeScript. This isn't an exhaustive list, and future chapters will describe more ways to name and use other types.

Types can also appear in many more places than just type annotations. As we learn about the types themselves, we'll also learn about the places where we can refer to these types to form new constructs.

We'll start by reviewing the most basic and common types you might encounter when writing JavaScript or TypeScript code. These will later form the core building blocks of more complex types.

hashtag
The primitives:string,number, andboolean

JavaScript has three very commonly used [primitives](https: //developer.mozilla.org/en-US/docs/Glossary/Primitive): string, number, and boolean. Each has a corresponding type in TypeScript. As you might expect, these are the same names you'd see if you used the JavaScript typeof operator on a value of those types:

  • string represents string values like "Hello, world"

  • number is for numbers like 42. JavaScript does not have a special runtime value for integers, so there's no equivalent to int or float - everything is simply number

The type names String, Number, and Boolean (starting with capital letters) are legal, but refer to some special built-in types that will very rarely appear in your code. Always use string, number, or boolean for types.

hashtag
Arrays

To specify the type of an array like [1, 2, 3], you can use the syntax number[]; this syntax works for any type (e.g. string[] is an array of strings, and so on). You may also see this written as Array<number>, which means the same thing. We'll learn more about the syntax T<U> when we cover generics.

Note that [number] is a different thing; refer to the section on [Tuples](https: //www.typescriptlang.org/docs/handbook/2/objects.html#tuple-types).

hashtag
any

TypeScript also has a special type, any, that you can use whenever you don't want a particular value to cause typechecking errors.

When a value is of type any, you can access any properties of it (which will in turn be of type any), call it like a function, assign it to (or from) a value of any type, or pretty much anything else that's syntactically legal:

The any type is useful when you don't want to write out a long type just to convince TypeScript that a particular line of code is okay.

hashtag
noImplicitAny

When you don't specify a type, and TypeScript can't infer it from context, the compiler will typically default to any.

You usually want to avoid this, though, because any isn't type-checked. Use the compiler flag [noImplicitAny](https: //www.typescriptlang.org/tsconfig#noImplicitAny) to flag any implicit any as an error.

hashtag
Type Annotations on Variables

When you declare a variable using const, var, or let, you can optionally add a type annotation to explicitly specify the type of the variable:

TypeScript doesn't use "types on the left"-style declarations like int x = 0; Type annotations will always go after the thing being typed.

In most cases, though, this isn't needed. Wherever possible, TypeScript tries to automatically infer the types in your code. For example, the type of a variable is inferred based on the type of its initializer:

For the most part you don't need to explicitly learn the rules of inference. If you're starting out, try using fewer type annotations than you think - you might be surprised how few you need for TypeScript to fully understand what's going on.

hashtag
Functions

Functions are the primary means of passing data around in JavaScript. TypeScript allows you to specify the types of both the input and output values of functions.

hashtag
Parameter Type Annotations

When you declare a function, you can add type annotations after each parameter to declare what types of parameters the function accepts. Parameter type annotations go after the parameter name:

When a parameter has a type annotation, arguments to that function will be checked:

Even if you don't have type annotations on your parameters, TypeScript will still check that you passed the right number of arguments.

hashtag
Return Type Annotations

You can also add return type annotations. Return type annotations appear after the parameter list:

Much like variable type annotations, you usually don't need a return type annotation because TypeScript will infer the function's return type based on its return statements. The type annotation in the above example doesn't change anything. Some codebases will explicitly specify a return type for documentation purposes, to prevent accidental changes, or just for personal preference.

hashtag
Anonymous Functions

Anonymous functions are a little bit different from function declarations. When a function appears in a place where TypeScript can determine how it's going to be called, the parameters of that function are automatically given types.

Here's an example:

Even though the parameter s didn't have a type annotation, TypeScript used the types of the forEach function, along with the inferred type of the array, to determine the type s will have.

This process is called contextual typing because the context that the function occurred within informs what type it should have.

Similar to the inference rules, you don't need to explicitly learn how this happens, but understanding that it does happen can help you notice when type annotations aren't needed. Later, we'll see more examples of how the context that a value occurs in can affect its type.

hashtag
Object Types

Apart from primitives, the most common sort of type you'll encounter is an object type. This refers to any JavaScript value with properties, which is almost all of them! To define an object type, we simply list its properties and their types.

For example, here's a function that takes a point-like object:

Here, we annotated the parameter with a type with two properties - x and y - which are both of type number. You can use , or ; to separate the properties, and the last separator is optional either way.

The type part of each property is also optional. If you don't specify a type, it will be assumed to be any.

hashtag
Optional Properties

Object types can also specify that some or all of their properties are optional. To do this, add a ? after the property name:

In JavaScript, if you access a property that doesn't exist, you'll get the value undefined rather than a runtime error. Because of this, when you read from an optional property, you'll have to check for undefined before using it.

hashtag
Union Types

TypeScript's type system allows you to build new types out of existing ones using a large variety of operators. Now that we know how to write a few types, it's time to start combining them in interesting ways.

hashtag
Defining a Union Type

The first way to combine types you might see is a union type. A union type is a type formed from two or more other types, representing values that may be any one of those types. We refer to each of these types as the union's members.

Let's write a function that can operate on strings or numbers:

hashtag
Working with Union Types

It's easy to provide a value matching a union type - simply provide a type matching any of the union's members. If you have a value of a union type, how do you work with it?

TypeScript will only allow an operation if it is valid for every member of the union. For example, if you have the union string | number, you can't use methods that are only available on string:

The solution is to narrow the union with code, the same as you would in JavaScript without type annotations. Narrowing occurs when TypeScript can deduce a more specific type for a value based on the structure of the code.

For example, TypeScript knows that only a string value will have a typeof value "string":

Another example is to use a function like Array.isArray:

Notice that in the else branch, we don't need to do anything special - if x wasn't a string[], then it must have been a string.

Sometimes you'll have a union where all the members have something in common. For example, both arrays and strings have a slice method. If every member in a union has a property in common, you can use that property without narrowing:

It might be confusing that a union of types appears to have the intersection of those types' properties. This is not an accident - the name union comes from type theory. The union number | string is composed by taking the union of the values from each type. Notice that given two sets with corresponding facts about each set, only the intersection of those facts applies to the union of the sets themselves. For example, if we had a room of tall people wearing hats, and another room of Spanish speakers wearing hats, after combining those rooms, the only thing we know about every person is that they must be wearing a hat.

hashtag
Type Aliases

We've been using object types and union types by writing them directly in type annotations. This is convenient, but it's common to want to use the same type more than once and refer to it by a single name.

A type alias is exactly that - a name for any type. The syntax for a type alias is:

You can actually use a type alias to give a name to any type at all, not just an object type. For example, a type alias can name a union type:

Note that aliases are only aliases - you cannot use type aliases to create different/distinct "versions" of the same type. When you use the alias, it's exactly as if you had written the aliased type. In other words, this code might look illegal, but is OK according to TypeScript because both types are aliases for the same type:

hashtag
Interfaces

An interface declaration is another way to name an object type:

Just like when we used a type alias above, the example works just as if we had used an anonymous object type. TypeScript is only concerned with the structure of the value we passed to printCoord - it only cares that it has the expected properties. Being concerned only with the structure and capabilities of types is why we call TypeScript a structurally typed type system.

hashtag
Differences Between Type Aliases and Interfaces

Type aliases and interfaces are very similar, and in many cases you can choose between them freely. Almost all features of an interface are available in type, the key distinction is that a type cannot be re-opened to add new properties vs an interface which is always extendable.

|

Extending an interface

|

Extending a type via intersections

| |

Adding new fields to an existing interface

|

A type cannot be changed after being created

|

You'll learn more about these concepts in later chapters, so don't worry if you don't understand all of these right away.

  • Prior to TypeScript version 4.2, type alias names [may appear in error messages](https: //www.typescriptlang.org/play?#code/PTAEGEHsFsAcEsA2BTATqNrLusgzngIYDm+oA7koqIYuYQJ56gCueyoAUCKAC4AWHAHaFcoSADMaQ0PCG80EwgGNkALk6c5C1EtWgAsqOi1QAb06groEbjWg8vVHOKcAvpokshy3vEgyyMr8kEbQJogAFND2YREAlOaW1soBeJAoAHSIkMTRmbbI8e6aPMiZxJmgACqCGKhY6ABGyDnkFFQ0dIzMbBwCwqIccabcYLyQoKjIEmh8kwN8DLAc5PzwwbLMyAAeK77IACYaQSEjUWZWhfYAjABMAMwALA+gbsVjoADqgjKESytQPxCHghAByXigYgBfr8LAsYj8aQMUASbDQcRSExCeCwFiIQh+AKfAYyBiQFgOPyIaikSGLQo0Zj-aazaY+dSaXjLDgAGXgAC9CKhDqAALxJaw2Ib2RzOISuDycLw+ImBYKQflCkWRRD2LXCw6JCxS1JCdJZHJ5RAFIbFJU8ADKC3WzEcnVZaGYE1ABpFnFOmsFhsil2uoHuzwArO9SmAAEIsSFrZB-GgAjjA5gtVN8VCEc1o1C4Q4AGlR2AwO1EsBQoAAbvB-gJ4HhPgB5aDwem-Ph1TCV3AEEirTp4ELtRbTPD4vwKjOfAuioSQHuDXBcnmgACC+eCONFEs73YAPGGZVT5cRyyhiHh7AAON7lsG3vBggB8XGV3l8-nVISOgghxoLq9i7io-AHsayRWGaFrlFauq2rg9qaIGQHwCBqChtKdgRo8TxRjeyB3o+7xAA), sometimes in place of the equivalent anonymous type (which may or may not be desirable). Interfaces will always be named in error messages.

  • Type aliases may not participate [in declaration merging, but interfaces can](https: //www.typescriptlang.org/play?#code/PTAEEEDtQS0gXApgJwGYEMDGjSfdAIx2UQFoB7AB0UkQBMAoEUfO0Wgd1ADd0AbAK6IAzizp16ALgYM4SNFhwBZdAFtV-UAG8GoPaADmNAcMmhh8ZHAMMAvjLkoM2UCvWad+0ARL0A-GYWVpA29gyY5JAWLJAwGnxmbvGgALzauvpGkCZmAEQAjABMAMwALLkANBl6zABi6DB8okR4Jjg+iPSgABboovDk3jjo5pbW1d6+dGb5djLwAJ7UoABKiJTwjThpnpnGpqPBoTLMAJrkArj4kOTwYmycPOhW6AR8IrDQ8N04wmo4HHQCwYi2Waw2W1S6S8HX8gTGITsQA).

For the most part, you can choose based on personal preference, and TypeScript will tell you if it needs something to be the other kind of declaration. If you would like a heuristic, use interface until you need to use features from type.

hashtag
Type Assertions

Sometimes you will have information about the type of a value that TypeScript can't know about.

For example, if you're using document.getElementById, TypeScript only knows that this will return some kind of HTMLElement, but you might know that your page will always have an HTMLCanvasElement with a given ID.

In this situation, you can use a type assertion to specify a more specific type:

Like a type annotation, type assertions are removed by the compiler and won't affect the runtime behavior of your code.

You can also use the angle-bracket syntax (except if the code is in a .tsx file), which is equivalent:

Reminder: Because type assertions are removed at compile-time, there is no runtime checking associated with a type assertion. There won't be an exception or null generated if the type assertion is wrong.

TypeScript only allows type assertions which convert to a more specific or less specific version of a type. This rule prevents "impossible" coercions like:

Sometimes this rule can be too conservative and will disallow more complex coercions that might be valid. If this happens, you can use two assertions, first to any (or unknown, which we'll introduce later), then to the desired type:

hashtag
Literal Types

In addition to the general types string and number, we can refer to specific strings and numbers in type positions.

One way to think about this is to consider how JavaScript comes with different ways to declare a variable. Both var and let allow for changing what is held inside the variable, and const does not. This is reflected in how TypeScript creates types for literals.

By themselves, literal types aren't very valuable:

It's not much use to have a variable that can only have one value!

But by combining literals into unions, you can express a much more useful concept - for example, functions that only accept a certain set of known values:

Numeric literal types work the same way:

Of course, you can combine these with non-literal types:

There's one more kind of literal type: boolean literals. There are only two boolean literal types, and as you might guess, they are the types true and false. The type boolean itself is actually just an alias for the union true | false.

hashtag
Literal Inference

When you initialize a variable with an object, TypeScript assumes that the properties of that object might change values later. For example, if you wrote code like this:

TypeScript doesn't assume the assignment of 1 to a field which previously had 0 is an error. Another way of saying this is that obj.counter must have the type number, not 0, because types are used to determine both reading and writing behavior.

The same applies to strings:

In the above example req.method is inferred to be string, not "GET". Because code can be evaluated between the creation of req and the call of handleRequest which could assign a new string like "GUESS" to req.method, TypeScript considers this code to have an error.

There are two ways to work around this.

  1. You can change the inference by adding a type assertion in either location:

// Change 1: const req = { url: "https: //example.com", method: "GET" as "GET" }; // Change 2handleRequest(req.url, req.method as "GET");Try

The as const suffix acts like const but for the type system, ensuring that all properties are assigned the literal type instead of a more general version like string or number.

hashtag
nullandundefined

JavaScript has two primitive values used to signal absent or uninitialized value: null and undefined.

TypeScript has two corresponding types by the same names. How these types behave depends on whether you have the [strictNullChecks](https: //www.typescriptlang.org/tsconfig#strictNullChecks) option on.

hashtag
strictNullChecksoff

With [strictNullChecks](https: //www.typescriptlang.org/tsconfig#strictNullChecks) off, values that might be null or undefined can still be accessed normally, and the values null and undefined can be assigned to a property of any type. This is similar to how languages without null checks (e.g. C#, Java) behave. The lack of checking for these values tends to be a major source of bugs; we always recommend people turn [strictNullChecks](https: //www.typescriptlang.org/tsconfig#strictNullChecks) on if it's practical to do so in their codebase.

hashtag
strictNullCheckson

With [strictNullChecks](https: //www.typescriptlang.org/tsconfig#strictNullChecks) on, when a value is null or undefined, you will need to test for those values before using methods or properties on that value. Just like checking for undefined before using an optional property, we can use narrowing to check for values that might be null:

hashtag
Non-null Assertion Operator (Postfix!)

TypeScript also has a special syntax for removing null and undefined from a type without doing any explicit checking. Writing ! after any expression is effectively a type assertion that the value isn't null or undefined:

Just like other type assertions, this doesn't change the runtime behavior of your code, so it's important to only use ! when you know that the value can't be null or undefined.

hashtag
Enums

Enums are a feature added to JavaScript by TypeScript which allows for describing a value which could be one of a set of possible named constants. Unlike most TypeScript features, this is not a type-level addition to JavaScript but something added to the language and runtime. Because of this, it's a feature which you should know exists, but maybe hold off on using unless you are sure. You can read more about enums in the [Enum reference page](https: //www.typescriptlang.org/docs/handbook/enums.html).

hashtag
Less Common Primitives

It's worth mentioning the rest of the primitives in JavaScript which are represented in the type system. Though we will not go into depth here.

bigint

From ES2020 onwards, there is a primitive in JavaScript used for very large integers, BigInt:

You can learn more about BigInt in [the TypeScript 3.2 release notes](https: //www.typescriptlang.org/docs/handbook/release-notes/typescript-3-2.html#bigint).

symbol

There is a primitive in JavaScript used to create a globally unique reference via the function Symbol():

You can learn more about them in [Symbols reference page](https: //www.typescriptlang.org/docs/handbook/symbols.html).

Typescript Interfaces

When we talk about a type in TypeScript, we mean a collection of things that you can do with a variable (or expression). You might be able to read or write a given property, call a function, use the expression as a constructor, or index into the object. Some objects (like Date) in JavaScript can do nearly all of those! In TypeScript, interfaces are the most flexible way of describing types.

You'll see interfaces used to describe existing JavaScript APIs, create shorthand names for commonly-used types, constrain class implementations, describe array types, and more. While they don't generate any code (and thus have no runtime cost!), they are often the key point of contact between any two pieces of TypeScript code, especially when working with existing JavaScript code or built-in JavaScript objects.

The only job of an interface in TypeScript is to describe a type. While class and function deal with implementation, interface helps us keep our programs error-free by providing information about the shape of the data we work with. Because the type information is erased from a TypeScript program during compilation, we can freely add type data using interfaces without worrying about the runtime overhead.

While that sounds like a simple, one-purpose task, interfaces role in describing types becomes manifest in a large variety of ways. Let's look at some of them and how they can be used in TypeScript programs.

Types VS Interfaces

hashtag
Types vs. interfaces in TypeScript - LogRocket Blog

Excerpt

It is very simple to get started with TypeScript, but sometimes we need to think more about the best use case for us. In this case, types or interfaces?


Enums

hashtag
Enums

Enums are one of the few features TypeScript has which is not a type-level extension of JavaScript.

Enums allow a developer to define a set of named constants. Using enums can make it easier to document intent, or create a set of distinct cases. TypeScript provides both numeric and string-based enums.

Types VS Interfaces

hashtag
Object Types

In JavaScript, the fundamental way that we group and pass around data is through objects. In TypeScript, we represent those through object types.

As we've seen, they can be anonymous:

or they can be named by using either an interface

The idea of having static type-checking in JavaScript is really fantastic and the adoption of TypeScript is growing more every day.

You started to use TypeScript in your project, you created your first type, then you jumped to your first interface, and you got it working. You concluded that TypeScript, in fact, was helping your development and saving you precious time, but you might have made some mistakes and not followed the best practices when you started to work with types and interfaces in TypeScript.

This is the case for a lot of developers, they don't really know the real difference between type aliases and interfaces in TypeScript.

It is very simple to get started with TypeScript, but sometimes we need to think more about the best use case for us. In this case, types or interfaces?

hashtag
Types and type aliases

Before we jump into the differences between types and interfaces in TypeScript, we need to understand something.

In TypeScript, we have a lot of basic types, such as string, boolean, and number. These are the basic types of TypeScript. You can check the list of all the basic types [here](https: //www.typescriptlang.org/docs/handbook/basic-types.html#table-of-contents). Also, in TypeScript, we have advanced types and in these [advanced types](https: //www.typescriptlang.org/docs/handbook/advanced-types.html), we have something called [type aliases](https: //www.typescriptlang.org/docs/handbook/advanced-types.html#type-aliases). With type aliases, we can create a new name for a type but we don't define a new type.

We use the type keyword to create a new type alias, that's why some people might get confused and think that it's creating a new type when they're only creating a new name for a type. So, when you hear someone talking about the differences between types and interfaces, like in this article, you can assume that this person is talking about type aliases vs interfaces.

We will use the [TypeScript Playground](https: //www.typescriptlang.org/play/index.html#) for code examples. The [TypeScript Playground](https: //www.typescriptlang.org/play/index.html#) allows us to work and test the latest version of TypeScript (or any other version we want to), and we will save time by using this playground rather than creating a new TypeScript project just for examples.

hashtag
Types vs. interfaces

The difference between types and interfaces in TypeScript used to be more clear, but with the latest versions of TypeScript, they're becoming more similar.

Interfaces are basically a way to describe data shapes, for example, an object.

Type is a definition of a type of data, for example, a union, primitive, intersection, tuple, or any other type.

hashtag
Declaration merging

One thing that's possible to do with interfaces but are not with types is declaration merging. Declaration merging happens when the TypeScript compiler merges two or more interfaces that share the same name into only one declaration.

Let's imagine that we have two interfaces called Song, with different properties:

interface Song { artistName: string; }; interface Song { songName: string; }; const song: Song = { artistName: "Freddie", songName: "The Chain" };

TypeScript will automatically merge both interfaces declarations into one, so when we use this Song interface, we'll have both properties.

Declaration merging does not work with types. If we try to create two types with the same names, but with different properties, TypeScript would still throw us an error.

Duplicate identifier Song.

hashtag
Extends and implements

In TypeScript, we can easily extend and implement interfaces. This is not possible with types though.

Interfaces in TypeScript can extend classes, this is a very awesome concept that helps a lot in a more object-oriented way of programming. We can also create classes implementing interfaces.

For example, let's imagine that we have a class called Car and an interface called NewCar, we can easily extend this class using an interface:

class Car { printCar = () => { console.log("this is my car") } }; interface NewCar extends Car { name: string; }; class NewestCar implements NewCar { name: "Car"; constructor(engine:string) { this.name = name } printCar = () => { console.log("this is my car") } };

hashtag
Intersection

Intersection allows us to combine multiple types into a single one type. To create an intersection type, we have to use the & keyword:

type Name = { name: "string" }; type Age = { age: number }; type Person = Name & Age;

The nice thing here is that we can create a new intersection type combining two interfaces, for example, but not the other way around. We cannot create an interface combining two types, because it doesn't work:

interface Name { name: "string" }; interface Age { age: number }; type Person = Name & Age;

hashtag
Unions

Union types allow us to create a new type that can have a value of one or a few more types. To create a union type, we have to use the | keyword.

type Man = { name: "string" }; type Woman = { name: "string" }; type Person = Man | Woman;

Similar to intersections, we can create a new union type combining two interfaces, for example, but not the other way around:

interface Man { name: "string" }; interface Woman { name: "string" }; type Person = Man | Woman;

hashtag
Tuples

[Tuples](https: //www.typescriptlang.org/docs/handbook/basic-types.html#tuple) are a very helpful concept in TypeScript, it brought to us this new data type that includes two sets of values of different data types.

type Reponse = [string, number]

But, in TypeScript, we can only declare tuples using types and not interfaces. There's no way we can declare a tuple in TypeScript using an interface, but you still are able to use a tuple inside an interface, like this:

interface Response { value: [string, number] }

We can see that we can achieve the same result as using types with interfaces. So, here comes the question that a lot of developers might have — should I use a type instead of an interface? If so, when should I use a type?

Let's understand the best use cases for both of them, so you don't need to abandon one for the other.

hashtag
What should I use?

This question is really tricky, and the answer to it, you might guess, depends on what you're building and what you're working on.

Interfaces are better when you need to define a new object or method of an object. For example, in React applications, when you need to define the props that a specific component is going to receive, it's ideal to use interface over types:

interface TodoProps { name: string; isCompleted: boolean }; const Todo: React.FC<TodoProps> = ({ name, isCompleted }) => { ... };

Types are better when you need to create functions, for example. Let's imagine that we have a function that's going to return an object called, type alias is more recommended for this approach:

type Person = { name: string, age: number }; type ReturnPerson = ( person: Person ) => Person; const returnPerson: ReturnPerson = (person) => { return person; };

At the end of the day, to decide if you should use a type alias or an interface, you should carefully think and analyze the situation — what you're working on, the specific code, etc.

Interface work better with objects and method objects, and types are better to work with functions, complex types, etc.

You should not start to use one and delete the other. Instead of doing that, start to refactor slowly, thinking of what makes more sense to that specific situation.

Remember that you can use both together and they will work fine. The idea here is just to clarify the differences between types and interfaces, and the best use cases for both.

hashtag
Conclusion

In this article, we learned more about the differences between types and interfaces in TypeScript. We learned that type aliases are advanced types in TypeScript, we learned the best use cases for both types and interfaces in TypeScript, and how we can apply both of them in real projects.

hashtag

  • boolean is for the two values true and false

  • Interfaces may only be used to [declare the shapes of objects, not rename primitives](https: //www.typescriptlang.org/play?#code/PTAEAkFMCdIcgM6gC4HcD2pIA8CGBbABwBtIl0AzUAKBFAFcEBLAOwHMUBPQs0XFgCahWyGBVwBjMrTDJMAshOhMARpD4tQ6FQCtIE5DWoixk9QEEWAeV37kARlABvaqDegAbrmL1IALlAEZGV2agBfampkbgtrWwMAJlAAXmdXdy8ff0Dg1jZwyLoAVWZ2Lh5QVHUJflAlSFxROsY5fFAWAmk6CnRoLGwmILzQQmV8JmQmDzI-SOiKgGV+CaYAL0gBBdyy1KCQ-Pn1AFFplgA5enw1PtSWS+vCsAAVAAtB4QQWOEMKBuYVUiVCYvYQsUTQcRSBDGMGmKSgAAa-VEgiQe2GLgKQA).

  • Interface names will [always appear in their original form](https: //www.typescriptlang.org/play?#code/PTAEGEHsFsAcEsA2BTATqNrLusgzngIYDm+oA7koqIYuYQJ56gCueyoAUCKAC4AWHAHaFcoSADMaQ0PCG80EwgGNkALk6c5C1EtWgAsqOi1QAb06groEbjWg8vVHOKcAvpokshy3vEgyyMr8kEbQJogAFND2YREAlOaW1soBeJAoAHSIkMTRmbbI8e6aPMiZxJmgACqCGKhY6ABGyDnkFFQ0dIzMbBwCwqIccabcYLyQoKjIEmh8kwN8DLAc5PzwwbLMyAAeK77IACYaQSEjUWY2Q-YAjABMAMwALA+gbsVjNXW8yxySoAADaAA0CCaZbPh1XYqXgOIY0ZgmcK0AA0nyaLFhhGY8F4AHJmEJILCWsgZId4NNfIgGFdcIcUTVfgBlZTOWC8T7kAJ42G4eT+GS42QyRaYbCgXAEEguTzeXyCjDBSAAQSE8Ai0Xsl0K9kcziExDeiQs1lAqSE6SyOTy0AKQ2KHk4p1V6s1OuuoHuzwArMagA) in error messages, but only when they are used by name.

  • Interface

    Type

    hashtag
    Basics

    To define an interface in TypeScript, use the interface keyword:

    This defines a type, Greetable, that has a member function called greet that takes a string argument. You can use this type in all the usual positions; for example in a parameter type annotation. Here we use that type annotation to get type safety on the g parameter:

    When this code compiles, you won't see any mention of Greetable in the JavaScript code. Interfaces are only a compile-time construct and have no effect on the generated code.

    hashtag
    Interfaces: TypeScript's Swiss Army Knife

    Interfaces get to play a lot of roles in TypeScript code. We'll go into more detail on these after a quick overview.

    Describing an Object

    Many JavaScript functions take a "settings object". For example, jQuery's $.ajax takes an object that can have up to several dozen members that control its behavior, but you're only likely to pass a few of those in any given instance. TypeScript interfaces allow optional properties to help you use these sorts of objects correctly.

    Describing an Indexable Object

    JavaScript freely mixes members (foo.x) with indexers (foo['x']), but most programmers use one or the other as a semantic hint about what kind of access is taking place. TypeScript interfaces can be used to represent what the expected type of an indexing operation is.

    Ensuring Class Instance Shape

    Often, you'll want to make sure that a class you're writing matches some existing surface area. This is how interfaces are used in more traditional OOP languages like C# and Java, and we'll see that TypeScript interfaces behave very similarly when used in this role.

    Ensuring the Static Shape of a Class or constructor Object

    Interfaces normally describe the shape of an instance of a class, but we can also use them to describe the static shape of the class (including its constructor function). We'll cover this in a later post.

    hashtag
    Describing an Object

    You can also use interfaces to define the shape of objects that will typically be expressed in an object literal. Here's an example:

    Describing Simple Types

    Note the use of the ? symbol after some of the names. This marks a member as being optional. This lets callers of createButton supply only the members they care about, while maintaining the constraint that the required parts of the object are present:

    You typically won't use optional members when defining interfaces that are going to be implemented by classes.

    Here's another example that shows an interesting feature of types in TypeScript:

    Note that we didn't annotate pt in any way to indicate that it's of type Point. We don't need to, because type checking in TypeScript is structural: types are considered identical if they have the same surface area. Because pt has at least the same members as Point, it's suitable for use wherever a Point is expected.

    Describing External Types

    Interfaces are also used to describe code that is present at runtime, but not implemented in the current TypeScript project. For example, if you open the lib.d.ts file that all TypeScript projects implicitly reference, you'll see an interface declaration for Number:

    Now if we have an expression of type Number, the compiler knows that it's valid to call toPrecision on that expression.

    Extending Existing Types

    Moreover, interfaces in TypeScript are open, meaning you can add your own members to an interface by simply writing another interface block. If you have an external script that adds members to Date, for example, you simply need to write interface Date { /*...*/ } and declare the additional members.*

    * Note: There are some known issues with the Visual Studio editor that currently prevent this scenario from working as intended. We'll be fixing this limitation in a later release.

    hashtag
    Describing an Indexable Object

    A common pattern in JavaScript is to use an object (e.g. {}) as way to map from a set of strings to a set of values. When those values are of the same type, you can use an interface to describe that indexing into an object always produces values of a certain type (in this case, Widget).

    hashtag
    Ensuring Class Instance Shape

    Let's extend the Greetable example above:

    We can implement this interface in a class using the implements keyword:

    Now we can use an instance of Person wherever a Greetable is expected:

    var g: Greetable = new Person();

    Similarly, we can take advantage of the structural typing of TypeScript to implement Greetable in an object literal:

    or a type alias.

    In all three examples above, we've written functions that take objects that contain the property name (which must be a string) and age (which must be a number).

    hashtag
    Property Modifiers

    Each property in an object type can specify a couple of things: the type, whether the property is optional, and whether the property can be written to.

    hashtag
    Optional Properties

    Much of the time, we'll find ourselves dealing with objects that might have a property set. In those cases, we can mark those properties as optional by adding a question mark (?) to the end of their names.

    In this example, both xPos and yPos are considered optional. We can choose to provide either of them, so every call above to paintShape is valid. All optionality really says is that if the property is set, it better have a specific type.

    We can also read from those properties - but when we do under [strictNullChecks](https: //www.typescriptlang.org/tsconfig#strictNullChecks), TypeScript will tell us they're potentially undefined.

    In JavaScript, even if the property has never been set, we can still access it - it's just going to give us the value undefined. We can just handle undefined specially.

    Note that this pattern of setting defaults for unspecified values is so common that JavaScript has syntax to support it.

    Here we used [a destructuring pattern](https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) for paintShape's parameter, and provided [default values](https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Default_values) for xPos and yPos. Now xPos and yPos are both definitely present within the body of paintShape, but optional for any callers to paintShape.

    Note that there is currently no way to place type annotations within destructuring patterns. This is because the following syntax already means something different in JavaScript.

    In an object destructuring pattern, shape: Shape means "grab the property shape and redefine it locally as a variable named Shape. Likewise xPos: number creates a variable named number whose value is based on the parameter's xPos.

    Using [mapping modifiers](https: //www.typescriptlang.org/docs/handbook/2/mapped-types.html#mapping-modifiers), you can remove optional attributes.

    hashtag
    readonlyProperties

    Properties can also be marked as readonly for TypeScript. While it won't change any behavior at runtime, a property marked as readonly can't be written to during type-checking.

    Using the readonly modifier doesn't necessarily imply that a value is totally immutable - or in other words, that its internal contents can't be changed. It just means the property itself can't be re-written to.

    It's important to manage expectations of what readonly implies. It's useful to signal intent during development time for TypeScript on how an object should be used. TypeScript doesn't factor in whether properties on two types are readonly when checking whether those types are compatible, so readonly properties can also change via aliasing.

    Using [mapping modifiers](https: //www.typescriptlang.org/docs/handbook/2/mapped-types.html#mapping-modifiers), you can remove readonly attributes.

    hashtag
    Index Signatures

    Sometimes you don't know all the names of a type's properties ahead of time, but you do know the shape of the values.

    In those cases you can use an index signature to describe the types of possible values, for example:

    Above, we have a StringArray interface which has an index signature. This index signature states that when a StringArray is indexed with a number, it will return a string.

    An index signature property type must be either ‘string' or ‘number'.

    chevron-rightIt is possible to support both types of indexers...hashtag

    While string index signatures are a powerful way to describe the "dictionary" pattern, they also enforce that all properties match their return type. This is because a string index declares that obj.property is also available as obj["property"]. In the following example, name's type does not match the string index's type, and the type checker gives an error:

    However, properties of different types are acceptable if the index signature is a union of the property types:

    Finally, you can make index signatures readonly in order to prevent assignment to their indices:

    You can't set myArray[2] because the index signature is readonly.

    hashtag
    Extending Types

    It's pretty common to have types that might be more specific versions of other types. For example, we might have a BasicAddress type that describes the fields necessary for sending letters and packages in the U.S.

    In some situations that's enough, but addresses often have a unit number associated with them if the building at an address has multiple units. We can then describe an AddressWithUnit.

    This does the job, but the downside here is that we had to repeat all the other fields from BasicAddress when our changes were purely additive. Instead, we can extend the original BasicAddress type and just add the new fields that are unique to AddressWithUnit.

    The extends keyword on an interface allows us to effectively copy members from other named types, and add whatever new members we want. This can be useful for cutting down the amount of type declaration boilerplate we have to write, and for signaling intent that several different declarations of the same property might be related. For example, AddressWithUnit didn't need to repeat the street property, and because street originates from BasicAddress, a reader will know that those two types are related in some way.

    interfaces can also extend from multiple types.

    hashtag
    Intersection Types

    interfaces allowed us to build up new types from other types by extending them. TypeScript provides another construct called intersection types that is mainly used to combine existing object types.

    An intersection type is defined using the & operator.

    Here, we've intersected Colorful and Circle to produce a new type that has all the members of Colorful and Circle.

    hashtag
    Interfaces vs. Intersections

    We just looked at two ways to combine types which are similar, but are actually subtly different. With interfaces, we could use an extends clause to extend from other types, and we were able to do something similar with intersections and name the result with a type alias. The principle difference between the two is how conflicts are handled, and that difference is typically one of the main reasons why you'd pick one over the other between an interface and a type alias of an intersection type.

    hashtag
    Generic Object Types

    Let's imagine a Box type that can contain any value - strings, numbers, Giraffes, whatever.

    Right now, the contents property is typed as any, which works, but can lead to accidents down the line.

    We could instead use unknown, but that would mean that in cases where we already know the type of contents, we'd need to do precautionary checks, or use error-prone type assertions.

    One type safe approach would be to instead scaffold out different Box types for every type of contents.

    But that means we'll have to create different functions, or overloads of functions, to operate on these types.

    That's a lot of boilerplate. Moreover, we might later need to introduce new types and overloads. This is frustrating, since our box types and overloads are all effectively the same.

    Instead, we can make a generic Box type which declares a type parameter.

    You might read this as "A Box of Type is something whose contents have type Type". Later on, when we refer to Box, we have to give a type argument in place of Type.

    Think of Box as a template for a real type, where Type is a placeholder that will get replaced with some other type. When TypeScript sees Box<string>, it will replace every instance of Type in Box<Type> with string, and end up working with something like { contents: string }. In other words, Box<string> and our earlier StringBox work identically.

    Box is reusable in that Type can be substituted with anything. That means that when we need a box for a new type, we don't need to declare a new Box type at all (though we certainly could if we wanted to).

    This also means that we can avoid overloads entirely by instead using [generic functions](https: //www.typescriptlang.org/docs/handbook/2/functions.html#generic-functions).

    It is worth noting that type aliases can also be generic. We could have defined our new Box<Type> interface, which was:

    by using a type alias instead:

    Since type aliases, unlike interfaces, can describe more than just object types, we can also use them to write other kinds of generic helper types.

    We'll circle back to type aliases in just a little bit.

    hashtag
    TheArrayType

    Generic object types are often some sort of container type that work independently of the type of elements they contain. It's ideal for data structures to work this way so that they're re-usable across different data types.

    It turns out we've been working with a type just like that throughout this handbook: the Array type. Whenever we write out types like number[] or string[], that's really just a shorthand for Array<number> and Array<string>.

    Much like the Box type above, Array itself is a generic type.

    Modern JavaScript also provides other data structures which are generic, like Map<K, V>, Set<T>, and Promise<T>. All this really means is that because of how Map, Set, and Promise behave, they can work with any sets of types.

    hashtag
    TheReadonlyArrayType

    The ReadonlyArray is a special type that describes arrays that shouldn't be changed.

    Much like the readonly modifier for properties, it's mainly a tool we can use for intent. When we see a function that returns ReadonlyArrays, it tells us we're not meant to change the contents at all, and when we see a function that consumes ReadonlyArrays, it tells us that we can pass any array into that function without worrying that it will change its contents.

    Unlike Array, there isn't a ReadonlyArray constructor that we can use.

    Instead, we can assign regular Arrays to ReadonlyArrays.

    Just as TypeScript provides a shorthand syntax for Array<Type> with Type[], it also provides a shorthand syntax for ReadonlyArray<Type> with readonly Type[].

    One last thing to note is that unlike the readonly property modifier, assignability isn't bidirectional between regular Arrays and ReadonlyArrays.

    hashtag
    Tuple Types

    A tuple type is another sort of Array type that knows exactly how many elements it contains, and exactly which types it contains at specific positions.

    Here, StringNumberPair is a tuple type of string and number. Like ReadonlyArray, it has no representation at runtime, but is significant to TypeScript. To the type system, StringNumberPair describes arrays whose 0 index contains a string and whose 1 index contains a number.

    If we try to index past the number of elements, we'll get an error.

    We can also [destructure tuples](https: //developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment#Array_destructuring) using JavaScript's array destructuring.

    Tuple types are useful in heavily convention-based APIs, where each element's meaning is "obvious". This gives us flexibility in whatever we want to name our variables when we destructure them. In the above example, we were able to name elements 0 and 1 to whatever we wanted.

    However, since not every user holds the same view of what's obvious, it may be worth reconsidering whether using objects with descriptive property names may be better for your API.

    Other than those length checks, simple tuple types like these are equivalent to types which are versions of Arrays that declare properties for specific indexes, and that declare length with a numeric literal type.

    Another thing you may be interested in is that tuples can have optional properties by writing out a question mark (? after an element's type). Optional tuple elements can only come at the end, and also affect the type of length.

    Tuples can also have rest elements, which have to be an array/tuple type.

    • StringNumberBooleans describes a tuple whose first two elements are string and number respectively, but which may have any number of booleans following.

    • StringBooleansNumber describes a tuple whose first element is string and then any number of booleans and ending with a number.

    • BooleansStringNumber describes a tuple whose starting elements any number of booleans and ending with a string then a number.

    A tuple with a rest element has no set "length" - it only has a set of well-known elements in different positions.

    Why might optional and rest elements be useful? Well, it allows TypeScript to correspond tuples with parameter lists. Tuples types can be used in [rest parameters and arguments](https: //www.typescriptlang.org/docs/handbook/2/functions.html#rest-parameters-and-arguments), so that the following:

    is basically equivalent to:

    This is handy when you want to take a variable number of arguments with a rest parameter, and you need a minimum number of elements, but you don't want to introduce intermediate variables.

    hashtag
    readonlyTuple Types

    One final note about tuple types - tuples types have readonly variants, and can be specified by sticking a readonly modifier in front of them - just like with array shorthand syntax.

    As you might expect, writing to any property of a readonly tuple isn't allowed in TypeScript.

    Tuples tend to be created and left un-modified in most code, so annotating types as readonly tuples when possible is a good default. This is also important given that array literals with const assertions will be inferred with readonly tuple types.

    Here, distanceFromOrigin never modifies its elements, but expects a mutable tuple. Since point's type was inferred as readonly [3, 4], it won't be compatible with [number, number] since that type can't guarantee point's elements won't be mutated.

    let obj: any = { x: 0 };
    // None of the following lines of code will throw compiler errors.
    // Using `any` disables all further type checking, and it is assumed
    // you know the environment better than TypeScript.obj.foo();obj();obj.bar = 100;obj = "hello";
    const n: number = obj;Try
    let myName: string = "Alice";Try
    // No type annotation needed -- 'myName' inferred as type 'string'let myName = "Alice";Try
    // Parameter type annotationfunction greet(name: string) {  console.log("Hello, " + name.toUpperCase() + "!!");}Try
    // Would be a runtime error if executed!greet(42);Argument of type 'number' is not assignable to parameter of type 'string'.Argument of type 'number' is not assignable to parameter of type 'string'.Try
    function getFavoriteNumber(): number {  return 26;}Try
    // No type annotations here, but TypeScript can spot the bug
    const names = ["Alice", "Bob", "Eve"];
    // Contextual typing for functionnames.forEach(function (s) {  console.log(s.toUppercase());Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?});
    // Contextual typing also applies to arrow functionsnames.forEach((s) => {  console.log(s.toUppercase());Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?Property 'toUppercase' does not exist on type 'string'. Did you mean 'toUpperCase'?});Try
    // The parameter's type annotation is an object typefunction printCoord(pt: { x: number; y: number }) {  console.log("The coordinate's x value is " + pt.x);  console.log("The coordinate's y value is " + pt.y);}printCoord({ x: 3, y: 7 });Try
    function printName(obj: { first: string; last?: string }) {
    // ...}
    // Both OKprintName({ first: "Bob" });printName({ first: "Alice", last: "Alisson" });Try
    function printName(obj: { first: string; last?: string }) {
    // Error - might crash if 'obj.last' wasn't provided!  console.log(obj.last.toUpperCase());Object is possibly 'undefined'.Object is possibly 'undefined'.  if (obj.last !== undefined) {
    // OK    console.log(obj.last.toUpperCase());  }
    // A safe alternative using modern JavaScript syntax:  console.log(obj.last?.toUpperCase());}Try
    function printId(id: number | string) {  console.log("Your ID is: " + id);}
    // OKprintId(101);
    // OKprintId("202");
    // ErrorprintId({ myID: 22342 });Argument of type '{ myID: number; }' is not assignable to parameter of type 'string | number'.
      Type '{ myID: number; }' is not assignable to type 'number'.Argument of type '{ myID: number; }' is not assignable to parameter of type 'string | number'.
      Type '{ myID: number; }' is not assignable to type 'number'.Try
    function printId(id: number | string) {  console.log(id.toUpperCase());Property 'toUpperCase' does not exist on type 'string | number'.
      Property 'toUpperCase' does not exist on type 'number'.Property 'toUpperCase' does not exist on type 'string | number'.
      Property 'toUpperCase' does not exist on type 'number'.}Try
    function printId(id: number | string) {  if (typeof id === "string") {
    // In this branch, id is of type 'string'    console.log(id.toUpperCase());  } else {
    // Here, id is of type 'number'    console.log(id);  }}Try
    function welcomePeople(x: string[] | string) {  if (Array.isArray(x)) {
    // Here: 'x' is 'string[]'    console.log("Hello, " + x.join(" and "));  } else {
    // Here: 'x' is 'string'    console.log("Welcome lone traveler " + x);  }}Try
    // Return type is inferred as number[] | stringfunction getFirstThree(x: number[] | string) {  return x.slice(0, 3);}Try
    type Point = {  x: number;  y: number;};
    // Exactly the same as the earlier examplefunction printCoord(pt: Point) {  console.log("The coordinate's x value is " + pt.x);  console.log("The coordinate's y value is " + pt.y);} printCoord({ x: 100, y: 100 });Try
    type ID = number | string;Try
    type UserInputSanitizedString = string; function sanitizeInput(str: string): UserInputSanitizedString {  return sanitize(str);}
    // Create a sanitized inputlet userInput = sanitizeInput(getInput());
    // Can still be re-assigned with a string thoughuserInput = "new input";Try
    interface Point {  x: number;  y: number;} function printCoord(pt: Point) {  console.log("The coordinate's x value is " + pt.x);  console.log("The coordinate's y value is " + pt.y);} printCoord({ x: 100, y: 100 });Try
    interface Animal {
    name: string
    }
    interface Bear extends Animal {
    honey: boolean
    }
    
    const bear = getBear()
    bear.name
    bear.honey
    type Animal = {
    name: string
    }
    type Bear = Animal & {
    honey: boolean
    }
    
    const bear = getBear();
    bear.name;
    bear.honey;
    interface Window {
    title: string
    }
    interface Window {
    ts: TypeScriptAPI
    }
    
    const src = '
    const a = "Hello World"';
    window.ts.transpileModule(src, {});
    type Window = {
    title: string
    }
    type Window = {
    ts: TypeScriptAPI
    }
    
    // Error: Duplicate identifier 'Window'.
    const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;
    Try;
    const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");
    Try;
    const x = "hello" as number;Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.Conversion of type 'string' to type 'number' may be a mistake because neither type sufficiently overlaps with the other. If this was intentional, convert the expression to 'unknown' first.Try
    const a = expr as any as T;
    Try;
    let changingString = "Hello World";changingString = "Olá Mundo";
    // Because `changingString` can represent any possible string, that
    // is how TypeScript describes it in the type systemchangingString;      let changingString: string
    const
    constantString = "Hello World";
    // Because `
    constantString` can only represent 1 possible string, it
    // has a literal type representation
    constantString;
    const
    constantString: "Hello World"Try
    let x: "hello" = "hello";
    // OKx = "hello";
    // ...x = "howdy";Type '"howdy"' is not assignable to type '"hello"'.Type '"howdy"' is not assignable to type '"hello"'.Try
    function printText(s: string, alignment: "left" | "right" | "center") {
    // ...}printText("Hello, world", "left");printText("G'day, mate", "centre");Argument of type '"centre"' is not assignable to parameter of type '"left" | "right" | "center"'.Argument of type '"centre"' is not assignable to parameter of type '"left" | "right" | "center"'.Try
    function compare(a: string, b: string): -1 | 0 | 1 {  return a === b ? 0 : a > b ? 1 : -1;}Try
    interface Options {  width: number;}function configure(x: Options | "auto") {
    // ...}configure({ width: 100 });configure("auto");configure("automatic");Argument of type '"automatic"' is not assignable to parameter of type 'Options | "auto"'.Argument of type '"automatic"' is not assignable to parameter of type 'Options | "auto"'.Try
    const obj = { counter: 0 };
    if (someCondition) {
      obj.counter = 1;
    }
    Try;
    const req = { url: "https:
    //example.com", method: "GET" };handleRequest(req.url, req.method);Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.Try
        Change 1 means "I intend for `req.method` to always have the _literal type_ `"GET"`", preventing the possible assignment of `"GUESS"` to that field after. Change 2 means "I know for other reasons that `req.method` has the value `"GET"`".
    
    2.  You can use `as const` to convert the entire object to be type literals:
    
            ```
    
    const req = { url: "https:
    //example.com", method: "GET" } as
    const;handleRequest(req.url, req.method);Try
    function doSomething(x: string | null) {  if (x === null) {
    // do nothing  } else {    console.log("Hello, " + x.toUpperCase());  }}Try
    function liveDangerously(x?: number | null) {
    // No error  console.log(x!.toFixed());}Try
    // Creating a bigint via the BigInt function
    const oneHundred: bigint = BigInt(100);
    // Creating a BigInt via the literal syntax
    const anotherHundred: bigint = 100n;
    Try;
    const firstName = Symbol("name");
    const secondName = Symbol("name"); if (firstName === secondName) {This condition will always return 'false' since the types 'typeof firstName' and 'typeof secondName' have no overlap.This condition will always return 'false' since the types 'typeof firstName' and 'typeof secondName' have no overlap.
    // Can't ever happen}Try
    interface Greetable {    greet(message: string): void;}
    function helloEnglish(g: Greetable) {    g.greet('Hello there!');
    // OK    g.greet(42);
    // Not OK -- 42 is not a string    g.greep('Hi');
    // Not OK -- 'greep' is not a member of 'Greetable'}
    interface ButtonSettings {    text: string;    size?: { width: number; height: number; };    color?: string;}function createButton(settings: ButtonSettings) { ... }
    createButton({ text: "Submit" });
    // OKcreateButton({ text: 'Submit', size: { width: 70, height: 30 }});
    // OKcreateButton({ text: 'Submit', color: 43);
    // Not OK: 43 isn't a stringcreateButton({ text: 'Submit', size: { width: 70 });
    // Not OK: size needs a height as wellcreateButton({ color: 'Blue'});
    // Not OK: 'text' member is required
    interface Point {    x: number;    y: number;}function getQuadrant(pt: Point) { ... }var pt = { x: 0, y: -1 };getQuadrant(pt);
    // OK: pt has members x and y of type number
    interface Number {    toString(radix?: number): string;    toFixed(fractionDigits?: number): string;    toExponential(fractionDigits?: number): string;    toPrecision(precision: number): string;}
    interface WidgetMap {    [name: string]: Widget;}var map: WidgetMap = {};map['gear'] = new GearWidget();var w = map['gear'];
    // w is inferred to type Widget
    /** Represents an object that can be greeted */interface Greetable {    /** Used to welcome someone */    greet(message: string): void;    /** The preferred language of this object */    language: string;}
    class Person implements Greetable {    language = 'English';    greet(message: string) {        console.log(message);    }}
    var greeter = {    greet: (message: string) => { console.log(message) };    language: 'Any';};
    function greet(person: { name: string; age: number }) {  return "Hello " + person.name;}Try
    interface Person {  name: string;  age: number;} function greet(person: Person) {  return "Hello " + person.name;}Try
    type Person = {  name: string;  age: number;}; function greet(person: Person) {  return "Hello " + person.name;}Try
    interface PaintOptions {  shape: Shape;  xPos?: number;  yPos?: number;} function paintShape(opts: PaintOptions) {
    // ...}
    const shape = getShape();paintShape({ shape });paintShape({ shape, xPos: 100 });paintShape({ shape, yPos: 100 });paintShape({ shape, xPos: 100, yPos: 100 });Try
    function paintShape(opts: PaintOptions) {  let xPos = opts.xPos;                   (property) PaintOptions.xPos?: number | undefined  let yPos = opts.yPos;                   (property) PaintOptions.yPos?: number | undefined
    // ...}Try
    function paintShape(opts: PaintOptions) {  let xPos = opts.xPos === undefined ? 0 : opts.xPos;       let xPos: number  let yPos = opts.yPos === undefined ? 0 : opts.yPos;       let yPos: number
    // ...}Try
    function paintShape({ shape, xPos = 0, yPos = 0 }: PaintOptions) {  console.log("x coordinate at", xPos);                                  (parameter) xPos: number  console.log("y coordinate at", yPos);                                  (parameter) yPos: number
    // ...}Try
    function draw({ shape: Shape, xPos: number = 100 /*...*/ }) {  render(shape);Cannot find name 'shape'. Did you mean 'Shape'?Cannot find name 'shape'. Did you mean 'Shape'?  render(xPos);Cannot find name 'xPos'.Cannot find name 'xPos'.}Try
    interface SomeType {  readonly prop: string;} function doSomething(obj: SomeType) {
    // We can read from 'obj.prop'.  console.log(`prop has the value '${obj.prop}'.`);
    // But we can't re-assign it.  obj.prop = "hello";Cannot assign to 'prop' because it is a read-only property.Cannot assign to 'prop' because it is a read-only property.}Try
    interface Home {  readonly resident: { name: string; age: number };} function visitForBirthday(home: Home) {
    // We can read and update properties from 'home.resident'.  console.log(`Happy birthday ${home.resident.name}!`);  home.resident.age++;} function evict(home: Home) {
    // But we can't write to the 'resident' property itself on a 'Home'.  home.resident = {Cannot assign to 'resident' because it is a read-only property.Cannot assign to 'resident' because it is a read-only property.    name: "Victor the Evictor",    age: 42,  };}Try
    interface Person {  name: string;  age: number;} interface ReadonlyPerson {  readonly name: string;  readonly age: number;} let writablePerson: Person = {  name: "Person McPersonface",  age: 42,};
    // workslet readonlyPerson: ReadonlyPerson = writablePerson; console.log(readonlyPerson.age);
    // prints '42'writablePerson.age++;console.log(readonlyPerson.age);
    // prints '43'Try
    interface StringArray {  [index: number]: string;}
    const myArray: StringArray = getStringArray();
    const secondItem = myArray[1];
    const secondItem: stringTry
    interface NumberDictionary {  [index: string]: number;   length: number;
    // ok  name: string;Property 'name' of type 'string' is not assignable to 'string' index type 'number'.Property 'name' of type 'string' is not assignable to 'string' index type 'number'.}Try
    interface NumberOrStringDictionary {  [index: string]: number | string;  length: number;
    // ok, length is a number  name: string;
    // ok, name is a string}Try
    interface ReadonlyStringArray {  readonly [index: number]: string;} let myArray: ReadonlyStringArray = getReadOnlyStringArray();myArray[2] = "Mallory";Index signature in type 'ReadonlyStringArray' only permits reading.Index signature in type 'ReadonlyStringArray' only permits reading.Try
    interface BasicAddress {  name?: string;  street: string;  city: string;  country: string;  postalCode: string;}Try
    interface AddressWithUnit {  name?: string;  unit: string;  street: string;  city: string;  country: string;  postalCode: string;}Try
    interface BasicAddress {  name?: string;  street: string;  city: string;  country: string;  postalCode: string;} interface AddressWithUnit extends BasicAddress {  unit: string;}Try
    interface Colorful {  color: string;} interface Circle {  radius: number;} interface ColorfulCircle extends Colorful, Circle {}
    const cc: ColorfulCircle = {  color: "red",  radius: 42,};Try
    interface Colorful {  color: string;}interface Circle {  radius: number;} type ColorfulCircle = Colorful & Circle;Try
    function draw(circle: Colorful & Circle) {  console.log(`Color was ${circle.color}`);  console.log(`Radius was ${circle.radius}`);}
    // okaydraw({ color: "blue", radius: 42 });
    // oopsdraw({ color: "red", raidus: 42 });Argument of type '{ color: string; raidus: number; }' is not assignable to parameter of type 'Colorful & Circle'.
      Object literal may only specify known properties, but 'raidus' does not exist in type 'Colorful & Circle'. Did you mean to write 'radius'?Argument of type '{ color: string; raidus: number; }' is not assignable to parameter of type 'Colorful & Circle'.
      Object literal may only specify known properties, but 'raidus' does not exist in type 'Colorful & Circle'. Did you mean to write 'radius'?Try
    interface Box {  contents: any;}Try
    interface Box {  contents: unknown;} let x: Box = {  contents: "hello world",};
    // we could check 'x.contents'if (typeof x.contents === "string") {  console.log(x.contents.toLowerCase());}
    // or we could use a type assertionconsole.log((x.contents as string).toLowerCase());Try
    interface NumberBox {  contents: number;} interface StringBox {  contents: string;} interface BooleanBox {  contents: boolean;}Try
    function setContents(box: StringBox, newContents: string): void;function setContents(box: NumberBox, newContents: number): void;function setContents(box: BooleanBox, newContents: boolean): void;function setContents(box: { contents: any }, newContents: any) {  box.contents = newContents;}Try
    interface Box<Type> {  contents: Type;}Try
    let box: Box<string>;Try
    interface Box<Type> {  contents: Type;}interface StringBox {  contents: string;} let boxA: Box<string> = { contents: "hello" };boxA.contents;        (property) Box<string>.contents: string let boxB: StringBox = { contents: "world" };boxB.contents;        (property) StringBox.contents: stringTry
    interface Box<Type> {  contents: Type;} interface Apple {
    // ....}
    // Same as '{ contents: Apple }'.type AppleBox = Box<Apple>;Try
    function setContents<Type>(box: Box<Type>, newContents: Type) {  box.contents = newContents;}Try
    interface Box<Type> {  contents: Type;}Try
    type Box<Type> = {  contents: Type;};Try
    type OrNull<Type> = Type | null; type OneOrMany<Type> = Type | Type[]; type OneOrManyOrNull<Type> = OrNull<OneOrMany<Type>>;           type OneOrManyOrNull<Type> = OneOrMany<Type> | null type OneOrManyOrNullStrings = OneOrManyOrNull<string>;               type OneOrManyOrNullStrings = OneOrMany<string> | nullTry
    function doSomething(value: Array<string>) {
    // ...} let myArray: string[] = ["hello", "world"];
    // either of these work!doSomething(myArray);doSomething(new Array("hello", "world"));Try
    interface Array<Type> {  /**   * Gets or sets the length of the array.   */  length: number;   /**   * Removes the last element from an array and returns it.   */  pop(): Type | undefined;   /**   * Appends new elements to an array, and returns the new length of the array.   */  push(...items: Type[]): number;
    // ...}Try
    function doStuff(values: ReadonlyArray<string>) {
    // We can read from 'values'...
    const copy = values.slice();  console.log(`The first value is ${values[0]}`);
    // ...but we can't mutate 'values'.  values.push("hello!");Property 'push' does not exist on type 'readonly string[]'.Property 'push' does not exist on type 'readonly string[]'.}Try
    new ReadonlyArray("red", "green", "blue");'ReadonlyArray' only refers to a type, but is being used as a value here.'ReadonlyArray' only refers to a type, but is being used as a value here.Try
    const roArray: ReadonlyArray<string> = ["red", "green", "blue"];
    Try;
    function doStuff(values: readonly string[]) {
    // We can read from 'values'...
    const copy = values.slice();  console.log(`The first value is ${values[0]}`);
    // ...but we can't mutate 'values'.  values.push("hello!");Property 'push' does not exist on type 'readonly string[]'.Property 'push' does not exist on type 'readonly string[]'.}Try
    let x: readonly string[] = [];let y: string[] = []; x = y;y = x;The type 'readonly string[]' is 'readonly' and cannot be assigned to the mutable type 'string[]'.The type 'readonly string[]' is 'readonly' and cannot be assigned to the mutable type 'string[]'.Try
    type StringNumberPair = [string, number];Try
    function doSomething(pair: [string, number]) {
    const a = pair[0];
    const a: string
    const b = pair[1];
    const b: number
    // ...} doSomething(["hello", 42]);Try
    function doSomething(pair: [string, number]) {
    // ...
    const c = pair[2];Tuple type '[string, number]' of length '2' has no element at index '2'.Tuple type '[string, number]' of length '2' has no element at index '2'.}Try
    function doSomething(stringHash: [string, number]) {
    const [inputString, hash] = stringHash;   console.log(inputString);
    const inputString: string   console.log(hash);
    const hash: number}Try
    interface StringNumberPair {
    // specialized properties  length: 2;  0: string;  1: number;
    // Other 'Array<string | number>' members...  slice(start?: number, end?: number): Array<string | number>;}Try
    type Either2dOr3d = [number, number, number?]; function setCoordinate(coord: Either2dOr3d) {
    const [x, y, z] = coord;
    const z: number | undefined   console.log(`Provided coordinates had ${coord.length} dimensions`);                                                  (property) length: 2 | 3}Try
    type StringNumberBooleans = [string, number, ...boolean[]];type StringBooleansNumber = [string, ...boolean[], number];type BooleansStringNumber = [...boolean[], string, number];Try
    const a: StringNumberBooleans = ["hello", 1];
    const b: StringNumberBooleans = ["beautiful", 2, true];
    const c: StringNumberBooleans = ["world", 3, true, false, true, false, true];
    Try;
    function readButtonInput(...args: [string, number, ...boolean[]]) {
    const [name, version, ...input] = args;
    // ...}Try
    function readButtonInput(name: string, version: number, ...input: boolean[]) {
    // ...}Try
    function doSomething(pair: readonly [string, number]) {
    // ...}Try
    function doSomething(pair: readonly [string, number]) {  pair[0] = "hello!";Cannot assign to '0' because it is a read-only property.Cannot assign to '0' because it is a read-only property.}Try
    let point = [3, 4] as
    const; function distanceFromOrigin([x, y]: [number, number]) {  return Math.sqrt(x ** 2 + y ** 2);} distanceFromOrigin(point);Argument of type 'readonly [3, 4]' is not assignable to parameter of type '[number, number]'.
      The type 'readonly [3, 4]' is 'readonly' and cannot be assigned to the mutable type '[number, number]'.Argument of type 'readonly [3, 4]' is not assignable to parameter of type '[number, number]'.
      The type 'readonly [3, 4]' is 'readonly' and cannot be assigned to the mutable type '[number, number]'.Try
    hashtag
    Numeric enums

    We'll first start off with numeric enums, which are probably more familiar if you're coming from other languages. An enum can be defined using the enum keyword.

    Above, we have a numeric enum where Up is initialized with 1 . All of the following members are auto-incremented from that point on. In other words, Direction.Up has the value 1 , Down has 2 , Left has 3 , and Right has 4 .

    If we wanted, we could leave off the initializers entirely:

    Here, Up would have the value 0 , Down would have 1 , etc. This auto-incrementing behavior is useful for cases where we might not care about the member values themselves, but do care that each value is distinct from other values in the same enum.

    Using an enum is simple: just access any member as a property off of the enum itself, and declare types using the name of the enum:

    Numeric enums can be mixed in [computed and constant members (see below)](https: //www.typescriptlang.org/docs/handbook/enums.html#computed-and- constant-members). The short story is, enums without initializers either need to be first, or have to come after numeric enums initialized with numeric constants or other constant enum members. In other words, the following isn't allowed:

    hashtag
    String enums

    String enums are a similar concept, but have some subtle [runtime differences](https: //www.typescriptlang.org/docs/handbook/enums.html#enums-at-runtime) as documented below. In a string enum, each member has to be constant-initialized with a string literal, or with another string enum member.

    While string enums don't have auto-incrementing behavior, string enums have the benefit that they "serialize" well. In other words, if you were debugging and had to read the runtime value of a numeric enum, the value is often opaque - it doesn't convey any useful meaning on its own (though [reverse mapping](https: //www.typescriptlang.org/docs/handbook/enums.html#reverse-mappings) can often help). String enums allow you to give a meaningful and readable value when your code runs, independent of the name of the enum member itself.

    hashtag
    Heterogeneous enums

    Technically enums can be mixed with string and numeric members, but it's not clear why you would ever want to do so:

    Unless you're really trying to take advantage of JavaScript's runtime behavior in a clever way, it's advised that you don't do this.

    hashtag
    Computed and

    constant members

    Each enum member has a value associated with it which can be either _ constant_ or computed. An enum member is considered constant if:

    • It is the first member in the enum and it has no initializer, in which case it's assigned the value 0:

    • The enum member is initialized with a constant enum expression. A constant enum expression is a subset of TypeScript expressions that can be fully evaluated at compile time. An expression is a constant enum expression if it is:

      1. a literal enum expression (basically a string literal or a numeric literal)

      2. a reference to previously defined constant enum member (which can originate from a different enum)

      3. a parenthesized constant enum expression

      4. one of the +, -, ~ unary operators applied to constant enum expression

      5. +, -, *, /, %, <<, >>, >>>, &, |, ^ binary operators with constant enum expressions as operands It is a compile time error for constant enum expressions to be evaluated to

    In all other cases enum member is considered computed.

    hashtag
    Union enums and enum member types

    There is a special subset of constant enum members that aren't calculated: literal enum members. A literal enum member is a constant enum member with no initialized value, or with values that are initialized to

    • any string literal (e.g. "foo", "bar, "baz")

    • any numeric literal (e.g. 1, 100)

    • a unary minus applied to any numeric literal (e.g. -1, -100)

    When all members in an enum have literal enum values, some special semantics come into play.

    The first is that enum members also become types as well! For example, we can say that certain members can only have the value of an enum member:

    The other change is that enum types themselves effectively become a union of each enum member. With union enums, the type system is able to leverage the fact that it knows the exact set of values that exist in the enum itself. Because of that, TypeScript can catch bugs where we might be comparing values incorrectly. For example:

    In that example, we first checked whether x was not E.Foo . If that check succeeds, then our || will short-circuit, and the body of the ‘if' will run. However, if the check didn't succeed, then x can only be E.Foo , so it doesn't make sense to see whether it's equal to E.Bar .

    hashtag
    Enums at runtime

    Enums are real objects that exist at runtime. For example, the following enum

    can actually be passed around to functions

    hashtag
    Enums at compile time

    Even though Enums are real objects that exist at runtime, the keyof keyword works differently than you might expect for typical objects. Instead, use keyof typeof to get a Type that represents all Enum keys as strings.

    hashtag
    Reverse mappings

    In addition to creating an object with property names for members, numeric enums members also get a reverse mapping from enum values to enum names. For example, in this example:

    TypeScript compiles this down to the following JavaScript:

    In this generated code, an enum is compiled into an object that stores both forward ( name -> value ) and reverse ( value -> name ) mappings. References to other enum members are always emitted as property accesses and never inlined.

    Keep in mind that string enum members do not get a reverse mapping generated at all.

    hashtag
    `

    const` enums

    In most cases, enums are a perfectly valid solution. However sometimes requirements are tighter. To avoid paying the cost of extra generated code and additional indirection when accessing enum values, it's possible to use const enums. const enums are defined using the const modifier on our enums:

    const enums can only use constant enum expressions and unlike regular enums they are completely removed during compilation. const enum members are inlined at use sites. This is possible since const enums cannot have computed members.

    in generated code will become

    ** const enum pitfalls**

    Inlining enum values is straightforward at first, but comes with subtle implications. These pitfalls pertain to ambient const enums only (basically const enums in .d.ts files) and sharing them between projects, but if you are publishing or consuming .d.ts files, these pitfalls likely apply to you, because tsc --declaration transforms .ts files into .d.ts files.

    1. For the reasons laid out in the [isolatedModules documentation](https: //www.typescriptlang.org/tsconfig#references-to- const-enum-members), that mode is fundamentally incompatible with ambient const enums. This means if you publish ambient const enums, downstream consumers will not be able to use [isolatedModules](https: //www.typescriptlang.org/tsconfig#isolatedModules) and those enum values at the same time.

    2. You can easily inline values from version A of a dependency at compile time, and import version B at runtime. Version A and B's enums can have different values, if you are not very careful, resulting in [surprising bugs](https: //github.com/microsoft/TypeScript/issues/5219#issue-110947903), like taking the wrong branches of if statements. These bugs are especially pernicious because it is common to run automated tests at roughly the same time as projects are built, with the same dependency versions, which misses these bugs completely.

    3. [importsNotUsedAsValues: "preserve"](https: //www.typescriptlang.org/tsconfig#importsNotUsedAsValues) will not elide imports for const enums used as values, but ambient const enums do not guarantee that runtime .js files exist. The unresolvable imports cause errors at runtime. The usual way to unambiguously elide imports, [type-only imports](https: //www.typescriptlang.org/docs/handbook/modules.html#importing-types), [does not allow const enum values](https: //github.com/microsoft/TypeScript/issues/40344), currently.

    Here are two approaches to avoiding these pitfalls:

    A. Do not use const enums at all. You can easily [ban const enums](https: //github.com/typescript-eslint/typescript-eslint/blob/master/docs/getting-started/linting/FAQ.md#how-can-i-ban-specific-language-feature) with the help of a linter. Obviously this avoids any issues with const enums, but prevents your project from inlining its own enums. Unlike inlining enums from other projects, inlining a project's own enums is not problematic and has performance implications. B. Do not publish ambient const enums, by de constifying them with the help of [preserve constEnums ](https: //www.typescriptlang.org/tsconfig#preserve constEnums). This is the approach taken internally by the [TypeScript project itself](https: //github.com/microsoft/TypeScript/pull/5422). [preserve constEnums ](https: //www.typescriptlang.org/tsconfig#preserve constEnums)emits the same JavaScript for const enums as plain enums. You can then safely strip the const modifier from .d.ts files [in a build step](https: //github.com/microsoft/TypeScript/blob/1a981d1df1810c868a66b3828497f049a944951c/Gulpfile.js#L144).

    This way downstream consumers will not inline enums from your project, avoiding the pitfalls above, but a project can still inline its own enums, unlike banning const enums entirely.

    hashtag
    Ambient enums

    Ambient enums are used to describe the shape of already existing enum types.

    One important difference between ambient and non-ambient enums is that, in regular enums, members that don't have an initializer will be considered constant if its preceding enum member is considered constant. By contrast, an ambient (and non- const) enum member that does not have an initializer is always considered computed.

    hashtag
    Objects vs Enums

    In modern TypeScript, you may not need an enum when an object with as const could suffice:

    \

    Typescript

    In typescript global types can be declared in a .d.ts file and used anywhere without explicitly importing them. Our project's .d.ts file is named project.d.ts .

    It contains:

    enum Direction {  Up = 1,  Down,  Left,  Right,}Try
    enum Direction {  Up,  Down,  Left,  Right,}Try
    enum UserResponse {  No = 0,  Yes = 1,} function respond(recipient: string, message: UserResponse): void {
    // ...} respond("Princess Caroline", UserResponse.Yes);Try
    enum E {  A = getSomeValue(),  B,Enum member must have initializer.Enum member must have initializer.}Try
    enum Direction {  Up = "UP",  Down = "DOWN",  Left = "LEFT",  Right = "RIGHT",}Try
    enum BooleanLikeHeterogeneousEnum {  No = 0,  Yes = "YES",}Try
    // E.X is
    constant:enum E {  X,}Try
        ```
    
    *   It does not have an initializer and the preceding enum member was a _numeric_
    constant. In this case the value of the current enum member will be the value of the preceding enum member plus one.
    // All enum members in 'E1' and 'E2' are
    constant. enum E1 {  X,  Y,  Z,} enum E2 {  A = 1,  B,  C,}Try
    ```
    enum FileAccess {
    //
    constant members  None,  Read = 1 << 1,  Write = 1 << 2,  ReadWrite = Read | Write,
    // computed member  G = "123".length,}Try
    enum ShapeKind {  Circle,  Square,} interface Circle {  kind: ShapeKind.Circle;  radius: number;} interface Square {  kind: ShapeKind.Square;  sideLength: number;} let c: Circle = {  kind: ShapeKind.Square,Type 'ShapeKind.Square' is not assignable to type 'ShapeKind.Circle'.Type 'ShapeKind.Square' is not assignable to type 'ShapeKind.Circle'.  radius: 100,};Try
    enum E {  Foo,  Bar,} function f(x: E) {  if (x !== E.Foo || x !== E.Bar) {This condition will always return 'true' since the types 'E.Foo' and 'E.Bar' have no overlap.This condition will always return 'true' since the types 'E.Foo' and 'E.Bar' have no overlap.
    //  }}Try
    enum E {
      X,
      Y,
      Z,
    }
    Try;
    enum E {
      X,
      Y,
      Z,
    }
    function f(obj: { X: number }) {
      return obj.X;
    }
    // Works, since 'E' has a property named 'X' which is a number.f(E);Try
    enum LogLevel {  ERROR,  WARN,  INFO,  DEBUG,} /** * This is equivalent to: * type LogLevelStrings = 'ERROR' | 'WARN' | 'INFO' | 'DEBUG'; */type LogLevelStrings = keyof typeof LogLevel; function printImportant(key: LogLevelStrings, message: string) {
    const num = LogLevel[key];  if (num <= LogLevel.WARN) {    console.log("Log level key is:", key);    console.log("Log level value is:", num);    console.log("Log level message is:", message);  }}printImportant("ERROR", "This is a message");Try
    enum Enum {
      A,
    }
    let a = Enum.A;
    let nameOfA = Enum[a];
    // "A"Try
    "use strict";
    var Enum;
    (function (Enum) {
      Enum[(Enum["A"] = 0)] = "A";
    })(Enum || (Enum = {}));
    let a = Enum.A;
    let nameOfA = Enum[a];
    // "A" Try
    const enum Enum {
      A = 1,
      B = A * 2,
    }
    Try;
    const enum Direction {
      Up,
      Down,
      Left,
      Right,
    }
    let directions = [
      Direction.Up,
      Direction.Down,
      Direction.Left,
      Direction.Right,
    ];
    Try;
    "use strict";
    let directions = [0 /* Up */, 1 /* Down */, 2 /* Left */, 3 /* Right */];
    Try;
    declare enum Enum {  A = 1,  B,  C = 2,}Try
    const enum EDirection {  Up,  Down,  Left,  Right,}
    const ODirection = {  Up: 0,  Down: 1,  Left: 2,  Right: 3,} as
    const; EDirection.Up;           (enum member) EDirection.Up = 0 ODirection.Up;           (property) Up: 0
    // Using the enum as a parameterfunction walk(dir: EDirection) {}
    // It requires an extra line to pull out the valuestype Direction = typeof ODirection[keyof typeof ODirection];function run(dir: Direction) {} walk(EDirection.Left);run(ODirection.Right);Try
    NaN
    or
    Infinity
    .

    Some library types in the form of [triple slash directives](https: //www.typescriptlang.org/docs/handbook/triple-slash-directives.html). These need to be placed at the top of the file.

  • Some library module declarations (usually these are included because these libs don't have typings but we still need to use them).

  • Our own global types.

  • Typescript provides many [Utility Types](https: //www.typescriptlang.org/docs/handbook/utility-types.html) which are useful for manipulating the base types in the global ComponentTypes interface.

    A few basic ones to know:

    hashtag
    Pick<Type, Keys>

    Only use the specified Keys from the Type.

    hashtag
    Partial<Type>

    Allows the type to be optional (undefined)

    hashtag
    Required<Type>

    Opposite of Partial, the type must be defined

    Using the stategies above you can select types from the global source and compose them to create a representation of the props in a specific component. While the global types live in project.d.ts , component level types should generally be placed in a types.ts file within the component directory and imported for use.

    Although ComponentTypes is a ✅ _Good starting place, some components may require a type that is more specific and not usefully included in the global declaration._


    hashtag
    Naming

    • {['class', 'enum', 'interface', 'namespace', 'type', 'variable-and-function'].map(item => (

    • {item.split('-').join(' ')}

    • ))}


    hashtag
    class

    🧑‍🔬 PascalCase

    🚫 Bad

    ✅ Good

    For memebers/methods use 🐪 camelCase

    🚫 Bad

    ✅ Good


    hashtag
    enum

    🧑‍🔬 PascalCase

    🚫 Bad

    ✅ Good


    hashtag
    interface

    🧑‍🔬 PascalCase

    🚫 Bad

    ✅ Good

    For memebers use 🐪 camelCase

    🚫 Bad

    ✅ Good


    hashtag
    namespace

    🧑‍🔬 PascalCase

    🚫 Bad

    ✅ Good


    hashtag
    type

    🧑‍🔬 PascalCase

    🚫 Bad

    ✅ ✅ Good


    hashtag
    variable and function

    🐪 camelCase

    🚫 Bad

    ✅ Good


    React | Typescript | Tailwind | Forms | Unit Tests

    Pick<ComponentTypes, "text">;
    
    // only use 'text' type
    Partial<Pick<ComponentTypes, "text">>;
    
    // only use 'text' type
    
    // the text type is optional
    Required<Pick<ComponentTypes, "text">>;
    
    // only use 'text' type
    
    // the text type is required
    class foo {}
    class Foo {}
    class Foo {
      Bar: number;
      BazQux() {}
    }
    class Foo {
      bar: number;
      bazQux() {}
    }
    enum backgroundColor {}
    enum BackgroundColor {}
    interface checkboxProps {}
    interface CheckboxProps {}
    interface CheckboxProps {
      IsSelected: boolean;
    }
    interface CheckboxProps = {
      isSelected: boolean;
    }
    namespace foo {}
    namespace Foo {}
    type imageProps = { src: string; alt: string };
    type ImageProps = { src: string; alt: string };
    const FooBar = "baz";
    
    const FooBar = () => "baz";
    const fooBar = "baz";
    
    const fooBar = () => "baz";