Basic Usage in TypeScript
Basic usage within Typescript
✅ If you're a web developer with little or no experience in TypeScript, this guide is perfect for you. We'll start with the basics, such as syntax and language concepts, in the "Basic usage within TypeScript" section. From there, we'll dive into more advanced topics, like ensuring type safety with the "Type check with Hash pattern object" and "Extract Type to Interface" sections. You'll learn how to optimize your code with the "Extract Type to Interface with Curry Function" and "How to define function with TypeScript" sections, which are geared towards more experienced users. This guide also covers other essential topics, such as "Type assertion with angle brackets," "Array with generic type T," "How can I exclude key or keys in TypeScript," and "TypeScript with type cast." By learning these key concepts, you'll be able to write robust and efficient TypeScript code that's perfect for your web development projects.
Type check with Hash pattern object
If direction is undefined, it might cause error. How to deal with that in typescript?
const directionStyle = {
up: "rotate-[-90deg]",
right: null,
down: "rotate-[90deg]",
left: "rotate-[180deg]",
};
directionStyle[direction]
If direction
is undefined
, accessing directionStyle[direction]
will throw a TypeError
because undefined
is not a property of directionStyle
. To avoid the error, you can check if direction
is truthy before accessing the property:
I use the following approach instead. I add applyDirectionClass
to prevent from wrong type
const directionStyle = {
up: "rotate-[-90deg]",
right: null,
down: "rotate-[90deg]",
left: "rotate-[180deg]",
};
let applyDirectionClass: string | null = null;
if(direction) {
applyDirectionClass = directionStyle[direction];
}
Extract Type to Interface
Extract them into isolated place?
type ConsoleMethod = (...args: any[]) => void;
Here's the corrected code:
declare namespace $projects {
export interface ConsoleMethod {
(...args: any[]): void;
}
}
The corrected code defines a namespace called $projects that contains an interface called ConsoleMethod. The ConsoleMethod interface describes a function type that takes in any number of arguments of any type (...args: any[]) and returns void.
Note that the correct syntax for defining a function type is using the () to specify the function's arguments and return type, rather than using = to assign a value to the interface. Additionally, the return type of the ConsoleMethod interface should be void, as the type describes a function that doesn't return a value.
Extract Type to Interface with Curry Function
// Define original function
export const consoleType = (type: keyof typeof console) => (env: "production" | "development" | "test") => {
if (env === "production") {
return () => {}
}
return (...args: any[]) => {
(console[type] as $utility.ConsoleMethod)(...args);
}
};
Extract inline function to type
type ConsoleType = (type: keyof typeof console) => (env: "production" | "development" | "test") => void;
It is OK too if you want to go type with declare namespace
declare namespace $utility {
type ConsoleType = (type: keyof typeof console) => (env: "production" | "development" | "test") => void;
const consoleType: ConsoleType;
}
Use declare namespace to define types and functions
declare namespace $utility {
export interface ConsoleMethod {
(...args: any[]): void
};
interface ConsoleType {
(type: keyof typeof console): (env: "production" | "development" | "test") => void;
}
}
Note that the ConsoleType
type is defined outside the namespace as it is used both inside and outside the namespace. The ConsoleType
type is then used to define the ConsoleType
type inside the namespace, as well as the consoleType
function. The ConsoleMethod
interface is also defined inside the namespace.
How to define function with typescript
To define a function with TypeScript, you can use the following syntax:
function functionName(parameter1: type1, parameter2: type2, ...): returnType {
// function body
}
Here's a breakdown of the different parts of the function definition:
function
: Thefunction
keyword is used to define a function.functionName
: This is the name of the function. You can use any valid identifier as the function name.(parameter1: type1, parameter2: type2, ...)
: This is the parameter list for the function. Each parameter is separated by a comma, and consists of a name and a type annotation.: returnType
: This is the return type of the function. The return type is specified after the parameter list, separated by a colon.{ // function body }
: This is the body of the function. It consists of one or more statements that are executed when the function is called.
Here's an example of a function that takes two parameters, x
and y
, both of type number
, and returns their sum:
function addNumbers(x: number, y: number): number {
return x + y;
}
How to define function with type and with interface separately?
interface Person {
firstName: string;
lastName: string;
}
type GreetingFunction = (person: Person) => string;
const sayHello: GreetingFunction = (person) => {
return `Hello, ${person.firstName} ${person.lastName}!`;
};
In this example, we define an interface Person
that specifies the shape of an object with a firstName
and lastName
property. Then we define a type GreetingFunction
that describes a function that takes a Person
as a parameter and returns a string.
Finally, we define a function sayHello
that matches the GreetingFunction
type, and takes a Person
object as a parameter. The function returns a string greeting the person by their first and last name.
Type assertion with angle brackets
The <string>
syntax in the code is a type assertion. A type assertion is a way to tell TypeScript that a value of a certain type should be treated as another type. In this case, it's asserting that addAllNumber(...props)
should be treated as a string
, even if it's actually a number
. This is necessary because the toFixed
method is only available on strings, and not on numbers.
By using a type assertion, the code is able to use the toFixed
method and fix the decimal places to 2. The result of the toFixed
method is then a string, which is the final return type of the function.
// I need to change this
export const addAllPrice = (...props: (number | string)[]): string =>
parseFloat(addAllNumber(...props)).toFixed(2);
// to this
export const addAllPrice = (...props: (number | string)[]): string =>
parseFloat(<string>addAllNumber(...props)).toFixed(2);
Why wrong?
const a = <string>[1, 2, 3];
The syntax <string>
is called type assertion, and it is used to inform TypeScript that a value has a specific type. In this case, it is trying to assert that the array [1, 2, 3]
is of type string
, but arrays can only be of a single type, and the elements of this array are of type number
, not string
. So this code will result in a type error.
Array with generic type T
How can I declare with T[]
export const popLast = (arr: any[]) => {
let result = arr;
result.pop();
return result;
};
You can declare the popLast
function to take an array of generic type T
as an argument and return an array of the same type T[]
as follows:
export const popLast = <T>(arr: T[]): T[] => {
let result = arr.slice(); // Create a copy of the array to avoid modifying the original array.
result.pop();
return result;
};
In this implementation, the popLast
function takes an array of type T[]
as input and returns an array of the same type T[]
. The generic type T
allows you to use this function with arrays of any type. The slice
method is used to create a copy of the input array to avoid modifying the original array. The pop
method is then called on the copied array, and the modified array is returned as the result.
In TypeScript, adding <T>
before a function name is how you declare that the function is a generic function.
By doing this, you are telling TypeScript that the function can take in an argument of any type, and it will return a value of the same type.
When you add <T>
to the function declaration, it allows you to use the type T
as a placeholder for a type that will be specified later, when the function is called.
For example, if you call the function popLast
and pass in an array of strings, TypeScript will infer the type string[]
for T
and use that type for the array arr
and the return value.
Using generics can make your code more reusable and more type-safe.
How can I exclude key or keys in Typescript
You can exclude one or more keys from a TypeScript type by using the Omit
utility type. Omit
creates a new type that includes all properties of the original type except for the specified keys.
Here's an example of how to use Omit
:
interface Person {
name: string;
age: number;
address: string;
}
// create a new type that excludes the "address" key from the Person interface
type PersonWithoutAddress = Omit<Person, 'address'>;
// usage
const person: PersonWithoutAddress = {
name: 'John',
age: 30,
};
In this example, we define an interface called Person
with three properties: name
, age
, and address
. We then create a new type called PersonWithoutAddress
that excludes the address
property from the Person
interface using the Omit
utility type.
You can use Omit
with any TypeScript type that has named properties, including object types, interface types, and class types.
If you want to delete multiple keys, you can do it like the following.
type TransformOrderBillingAddressProp = Omit<Order.BillingAddress, "address_2" | "email" | "phone" | "company" | "country_iso2">;
The syntax for Omit
type in TypeScript is to specify the type to be extended as the first argument, followed by a union of the keys to be omitted as the remaining arguments. Not this:
type TransformOrderBillingAddressProp = Omit<Order.BillingAddress, "address_2", "email", "phone", "company", "country_iso2”>;
Typescript with type cast
Operator '+' cannot be applied to types 'string | number' and 'number'.
The +
operator can't be applied to a value of type string | number
because JavaScript cannot automatically convert a string
to a number
. You need to handle both cases separately. You can use the Number()
function to convert a string
to a number
, or use a type guard to check the type of each value before adding it to the result. Here's one way to do that:
// I need to change this
export const addAllNumber = (...props: (string | number)[]) =>
props.reduce((a, b) => {
return a + naNToZero(+b);
}, 0);
// to this
export const addAllNumbers = (...props: (string | number)[]) => {
let sum = 0;
for (const prop of props) {
if (typeof prop === "string") {
sum += Number(prop) || 0;
} else {
sum += prop;
}
}
return sum;
};
We need to understand why addAllNumber
is incorrect. addAllNumber
is a function that takes a variable number of arguments of either string
or number
type, and returns their sum. The function uses the reduce
method to iterate over the array of arguments and add each element to a running total, represented by a
. However, this implementation is incorrect and leads to an error because a
may sometimes be of string
type, and the +
operator cannot be applied to a string
and a number
.
export const addAllNumber = (...props: (string | number)[]) =>
props.reduce((a, b) => {
return a + naNToZero(+b);
}, 0);
Two simple examples are provided to illustrate the concept. In both examples, using the +
operator on either a string or a number results in the expected outcome.
const a = "2";
console.log(+a);
const b = 2;
console.log(+b);
Additionally, using a for
loop with the +
operator works as expected.
const sumWithPlusSign = function (...props: (string | number)[]) {
let sum = 0;
for (const prop of props) {
sum += +prop;
}
return sum;
}
the for
loop is used to iterate over the props
array, adding each element to a running total (sum
). The +
operator is used to convert the prop
element to a number before it's added to the sum
. This ensures that if a string
is passed as an argument, it's typecast to a number
before being added to the sum
.
when i change it to reducer it causes error
const sumWithPlusSign = function (...props: (string | number)[]) {
return props.reduce((acc, curr) => acc + +curr, 0) // ❌
}
const sumWithPlusSign = function (...props: (string | number)[]) {
return props.reduce((acc, curr) => acc + Number(curr), 0) // ❌
}
It is incorrect in the expression acc + +curr
and it produces an error that states Operator '+' cannot be applied to types 'string | number' and 'number'
.
acc in this case could either be a string or a number
reduce
method is used to iterate over the props
array, adding each element to a running total (acc
). The +
operator is used to convert the curr
element to a number before it's added to the acc
. This is done inside the reduce function, which will accumulate the sum of all elements in the array.
The error you're encountering in the second example is because of the acc + +curr
expression. The error message is indicating that the +
operator cannot be applied to string | number
and number
. This is because reduce
can sometimes pass a string
type for curr
, and the +
operator cannot be applied to a string
and a number
. To resolve this, you'll need to make sure that curr
is always a number before adding it to acc
.
It will be right if I assign acc as number:
const sumWithPlusSign = function (...props: (string | number)[]) {
return props.reduce((acc: number, curr) => acc + +curr, 0)
}
const sumWithPlusSign = function (...props: (string | number)[]) {
return props.reduce((acc: number, curr) => acc + Number(curr), 0)
}
so i need to assign a with type number to suit both reduce and the outcome
export const addAllNumber = (...props: (string | number)[]) =>
props.reduce((a: number, b) => {
return a + naNToZero(+b);
}, 0);
Arbitrary Arguments in Function
type ArbitraryFunction = (...args: any[]) => any;
export const executeFunction = (env: "production" | "development" | "test") =>
(callback: ArbitraryFunction, ...arg: any)=> {
if (["development", "test"].includes(env)) {
callback(...arg);
}
}
export const executeInDev = executeFunction(process.env.VERCEL_ENV)
In order to define process.env.VERCEL_ENV
in TypeScript, you need to declare a global namespace that includes process.env
. Here is an example of how to define it:
declare global {
namespace NodeJS {
interface ProcessEnv {
VERCEL_ENV: string;
// add other environment variables as needed
}
}
}
This declares a new global namespace NodeJS
that contains the interface ProcessEnv
. This interface extends the existing NodeJS.ProcessEnv
interface to include the VERCEL_ENV
environment variable. You can add other environment variables to this interface as needed.
Note that this declaration must be in a .d.ts
file in order for TypeScript to recognize it.