TypeScript Flashcards
In dynamically typed languages, a variable’s type is determined when?
At runtime, based on the type of value it is assigned.
In statically typed languages, each variable must be assigned a specific data type when it is declared, and functions must specify the type of value they return.
In TypeScript, compile-time errors occur during…
the process of converting the TypeScript code into JavaScript code.
runtime errors occur in JavaScript when…
the code is being executed
The TypeScript compiler can detect both ____ and ____ before the code is executed.
syntax and type errors
What is type safety?
Type safety ensures that values in your code are used in a manner consistent with their declared types, helping catch potential errors at compile time rather than at runtime.
Changing existing JavaScript programs to TypeScript programs won’t “break” them, they will still compile and run. However, the TypeScript compiler will now throw a variety of helpful warnings and errors at build time, which we can resolve by updating our code and adding type annotations.
What are type annotations?
Type annotations are used to explicitly specify the type of the variable:
let myName: string = "Alice";
What will be logged to the console and why? How can we prevent this behavior?let numbers: Array<number> = [1, 2, 3, 4, 5];
let myNum: number = numbers[5];
console.log(myNum);
In the above code, we are able to assign the value undefined to the variable myNum, even though myNum has the type number. This seems like a bug, but TypeScript doesn’t raise a warning.
The issue is that TypeScript can’t know how long the array is, or will be. In other words, because JavaScript arrays have a dynamic length, the TypeScript compiler doesn’t know whether any particular index is out of bounds. As a result, it infers that any elements returned from the array are of the type the array contains.
You can add code to check if the variable is undefined
. Or you can use the noUncheckedIndexedAccess
compiler option in TypeScript.
What is the noUncheckedIndexedAccess
compiler option? Hint: .tsconfig
The noUncheckedInexedAccess
option is used to improve type safety by ensuring that all indexed access to object properties is properly checked. When this option is enabled, TypeScript treats every object property access using an index as potentially undefined, unless the type system can definitively determine that the property exists.
Tuples can be very useful in TypeScript when you want to…
work with a collection of values that have a specific order and data type.
let aTuple: [string, number, boolean] = ["Launch School", 1, true];
Primary differences between arrays and tuples?
Tuples have a fixed length.
You cannot access an out-of-bounds index of a tuple.
What happens if you call .push()
or .pop()
on a tuple? Why?
As long as you are pushing a valid type for the tuple, the TypeScript compiler will not raise an error. The problem is that tuples are just arrays, and thus they have all of the methods that arrays have. For this reason, it is generally considered a best practice to avoid using methods like push() and pop() when working with tuples as this can lead to unexpected behaviors.
This array consists of both string and boolean values. If you had to assign a type definition to myArray, what would it be?
const myArray = ["is", "launch school", "awesome", true, "or", false];
const myArray: (string | boolean)[] = [
"is",
"launch school",
"awesome",
true,
"or",
false,
];
____ is used as the return type for functions that don’t return a value.
void
What is type inference?
Type inference is a feature in TypeScript that allows the compiler to automatically deduce the types of variables when they are not explicitly specified.
What is meant by a method/function signature?
In TypeScript, a method signature (or function signature) defines:
1) the name of a method
2) the number and types of its parameters
3) the type of its return value.
It specifies how a method can be called, what arguments it takes, and what it returns, without providing the method’s implementation.
void
represents…
the absence of a value
Values with the void type cannot be…
1) assigned to any other data typefunction greet(name: string): void {
console.log(
Hello, ${name}!);
}
const result: void = greet("Jane");
// OKconst myUndefined: undefined = result; // Type 'void' is not assignable to type 'undefined'
2) cannot be used in a conditional to test truthiness.const myBool: boolean = result ? true : false; // An expression of type 'void' cannot be tested for truthiness.
Why does TypeScript raise an error with the following code?
function logSum(a: number, b: number): void {
const sum = a + b;
console.log("The sum of", a, "and", b, "is", sum);
return sum;
}
logSum(3, 4);
Because a function with a return type of void
is intended to not return a value and sum
is returned. If a function’s return type is void
and it has an explicit return it should only return undefined
, otherwise TypeScript will raise an error.
What is a literal type? List some specific examples.
In TypeScript literal types are used to describe the literal values that a variable can have.
Specific examples are string literals, numeric literals, and boolean literals.
let five: 5 = 5;
let hasName: true = true;
What is any
?
any
is a special TypeScript type that represents any possible value.
In TypeScript, type errors happen at…
compile-time
What is the following an example of?
type Person = { name: string; age: number };
The code snippet is an example of type aliasing.
What is type aliasing?
Type aliasing is when we define new custom types based on existing type using the type
keyword.
What is meant by an object’s shape?
The “shape” of an object refers to the structure of its properties and their types. When we talk about the shape of an object, we are referring to the names and types of the object’s properties.
In TypeScript’s structural typing rules, if we have two types, TypeA
and TypeB
, then TypeB
is assignable to TypeA
if…
type TypeA = { name: string; age: number };
type TypeB = { name: string; age: number; gpa: number };
it has at least the same members (properties and property types) as TypeA
.
In other words, TypeB
can have additional properties that aren’t available on TypeA
, but as long as it has the same properties and associated value types as TypeA
, then it is assignable.
*the exception to this rules is with object literalstype Employee = { name: string; age: number };
const jane: Employee = { name: "Jane Smith", age: 30, gpa: 4.0 }; // Type '{ name: string; age: number; gpa: number; }' is not assignable to type 'Employee'.
// Object literal may only specify known properties, and 'gpa' does not exist in type 'Employee'.
In TypeScript, readonly
properties are used to create properties that…
can only be set once during initialization and cannot be modified afterward. Once the value of a readonly property is set, it cannot be changed throughout the object’s lifetime.
Object.freeze()
Takes an object as an argument and freezes it. This prevents the object from being mutated. Any attempts to mutate the object will throw an error at runtime.
What are the characteristics of a ReadonlyArray
?
In TypeScript ReadonlyArray
’s cannot be mutated and do not have the typical array methods available to mutate them such as pop
, push
, and shift
.
Although developers coming from a JavaScript background tend to skip adding readonly to the properties of their types, we would encourage you to think carefully about whether each property’s value should change over the course of a program’s execution. If not, adding readonly will help you uncover and prevent serious issues later on.
What is the purpose of implements
?
In TypeScript, the implements
keyword is used to ensure that a class adheres to the contract defined by an interface. When a class implements an interface, it must provide concrete implementations for all the properties and methods defined in the interface.
Think of implements as a contract between the class definition and the interface. In order to fulfill the contract, you have to explicitly add the correct type information to the properties and methods.
What is private
?
The keyword private
is used to control the visibility of properties and methods within a class hierarchy.
What 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.
union types in TypeScript allow us to define a type that can hold more than one type
let unionDemo: (number | string) = 'hello';
TypeScript uses a structural typing system. This means…
TypeScript enforces type safety based on the shape of the types rather than their explicit declarations.
What is a type assertion?
Type assertion in TypeScript is a mechanism that allows you to override the inferred type of an expression and specify a different type that you know to be correct. It doesn’t perform any type checking or restructuring of data; it simply tells the TypeScript compiler to treat a value as a particular type. Type assertions are useful when you have more information about the type of a value than TypeScript can infer on its own.
What is a type predicate?
A type predicates is a special return value that allow a developer to create custom type guards.
To define a type predicate we simply define a function whose return type is a type predicate (syntax:[parameterName] is [type]
). When the function returns a truthy value, the TypeScript compiler will determine that the specified argument is of the given type.
the TypeScript compiler does not verify the logic within your type predicate function correctly determines the specified type
What are the five type guards?
typeof
in
truthinessinstanceof
type predicates (custom type guards)
What is a discriminant property?
A discriminant property is a common property for all members of a union with a unique literal value for each type in a union. A common practice is for the discriminant property value to be the name of the type in lowercase.
What is narrowing?
Narrowing is the process of refining a value from a larger set of possible types to a smaller set of possible types. Using narrowing TypeScript can deduce a more specific type for a value based on the structure of the code.
Can you explain why the following code does not contain any type errors?
function getFirstThree(x: number[] | string) { return x.slice(0, 3); }
When working with union types in TypeScript, you may only perform an operation (without narrowing) if the operation is valid for all members of the union. Because arrays and strings both have a .slice
method there is no type error and no narrowing is required.
What does assignability refer to?
In TypeScript assignability refers to the process of determining whether a value of one type can be assigned to a variable or parameter of another type.
How do narrow and wide types differ?
A narrow type refers to a type that is more specific and represents a smaller set of possible values. A wide type is more general and represents a larger set of possible values.
When working with union types, how does TypeScript determine if an operation is valid?
When working with union types, TypeScript will only allow an operation if it is valid for every member of the union.
Note that when working with objects, this means that TypeScript will only allow us to access properties that are available on all of the objects.
How does TypeScript infer the return type of a function?
In TypeScript, the return type of a function is inferred based on all possible return paths within the function. When a function can return different types depending on conditions, TypeScript infers the return type to be a union of all those possible types. This ensures that the function signature accurately represents all potential outputs, providing a comprehensive type safety.
What is a function overload?
Function overloads provide a way for the developer to express the relationship between an argument type and return type when a function takes many types of arguments.
Function overloads are composed of…
Two or more overload function signatures and an implementation signature.
What is an overload signature? What is required for an overload signature to be compatible?
Overload signatures are used to define the multiple variations of a function’s signature. This allows the function to accept parameters of varying types and return different types based on those parameters.
For an overload signature to be compatible:
1) Each parameter in an overload signature should have a corresponding parameter in the implementation signature.
2) The overload parameter type should be assignable to the implementation parameter type.
3) The overload return type should be assignable to the implementation return type.
What must we be cautious of when using function overloads?
When using function overloads the TypeScript compiler ensures that the overload signatures are compatible with the implementation signature.
TypeScript does not validate that the function’s implementation correctly implements the overload signatures.
Generics can be though of as…
all-purpose placeholders for a type that can be specified later.
The following is an example of a…
type User<T> = { name: string; age: T; };
Generic object
The following is an example of a…
function first<T>(arr: T[]): T { return arr[0]; }
Generic function.
The following is an example of a…
const numsAndStrings: Array<string | number> = [1, "2"];
Generic Array.
We could also use the syntax….
const numsAndStrings: (string | number)[] = [1, '2'];
Why is narrowing important?
Narrowing is necessary when a function is generic or accepts a wide set of possible types. Because the TypeScript compiler’s default behavior is to only allow access to properties/methods that are common between members of a given type unless we narrow a value’s type sufficiently so the TypeScript compiler determines we can safely access those properties/methods.
In TypeScript we can use the in
operator to narrow down an object based on…
its unique properties.
What is a discriminated union?
A discriminated union is a narrowing technique where a common discriminant property is added to each member of a union. This property can then be used to differentiate between the members of the union.
What are some benefits of using discriminated unions?
One benefit is that with the creation of a discriminant property to express the unique types is very explicit in the code. Another benefit is that when working with more complex objects you don’t need to comb through the shapes of all the union types to determine which properties could be used to narrow with the in
operator.
What is exhaustiveness checking?
Exhaustiveness checking is a feature in TypeScript that is used to ensure that all possible values of a discriminated union are handled in control flow constructs such as switch
statements and if/else
chains.
What is never
?
never
is a special type is TypeScript. Assigning any value to a variable of type never will lead to a type error.
How is exhaustiveness checking implemented?
Exhaustiveness checking is most commonly implemented within a conditional path that should never be reached using a combination of TypeScript’s never
type and JavaScript runtime errors.
const _exhaustiveCheck: never = animal;
return
Unknown animal: ${JSON.stringify(_exhaustiveCheck)};
Describe the assignability for a variable of type any
.
All types are assignable to a variable of type any
and a value of type any
can be assigned to every other type (except never
).
Using ____ essentially turns off type checking for a given value or assignment. This can lead to ____ ____, where TypeScript’s type system fails to catch a type error.
the type any
type unsoundness
When might it be appropriate to use any
?
1) when using 3rd party libraries that do not have TypeScript type definitions (or they are incomplete/inaccurate).
2) when migrating to TypeScript from vanilla JavaScript gradually.
What is the default type of function parameters if not specified?
any
What is type unsoundness?
Type unsoundness is when the type system fails to prevent type errors, resulting in runtime errors and bugs.
What are three common sources of type unsoundness mentioned in the curriculum?
1) any
as it essentially turns off type checking.
2) Incorrect type assertions
3) Accessing out-of-bounds index of an array.
What are the characteristics of the type unknown
?
unknown
is a type in TypeScript that is similar to any
in that any type may be assigned to it. However unknown
is safer than any
in that it is not assignable to any other type and you cannot perform any actions on it without first narrowing its type.
What is declaration merging?
Declaration merging refers to TypeScript’s ability to take two separate interface declarations that share the same name and create a single interface that merges the original ones.
Declaration merging combines the properties and methods from both declarations into one.
When declaration merging…If two declarations define the same member they must …
have the same type. Otherwise TypeScript will throw a type error.
How is extends
used with interfaces?
It essentially allows you to use an existing type as a base when defining a new type. When extends
is used with an interface it creates a new interface that includes all the properties of one or more existing interfaces.
Can you use extend
with type
?
The extends keyword only works with interface definitions, not type definitions. However a interface can extend a type
.
If you are extending an interface with another interface that has the same property…
the new property’s type must be assignable to the existing property type.
What is the role of the &
operator?
The &
operator is used to create a type intersection, a new type with the properties and methods of multiple types.
What happens if you create a type intersection with conflicting properties (no intersection between identical property types)?
If you create a type intersection with conflicting properties, TypeScript will not immediately inform the developer by throwing a type error. Instead the property will be given the type never
and only when you try to define a new value with the intersection type will a type error be thrown. This behavior makes type intersections less desirable to extending interfaces.
What is an interface?
An interface defines the shape of an object.
Index signatures are used to…
define a dynamic set of properties for an object.
The following is demonstrating the use of…
interface Accounts { [key: string]: string; }
Describe the code.
Index signatures.
Accounts is an interface with an index signature that allows any string key and the value type to be a string.
When adding named properties to an object type that utilizes index signatures, the named properties must have types that are…
assignable to the type in the index signature.
interface StringArray { [index: number]: string; }
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.
What is the proper syntax for utilizing an index signature?
{ [key: TYPE]: TYPE }
When indexed with TYPE it will return a value of TYPE
What is keyof
?
keyof
is a TypeScript operator that evaluates to a union of an interface’s properties. It is very useful for dynamically creating a new type based on the properties of an existing interface.
What is the syntax for defining a generic (function) constraint?
A generic constraint is defined using the extends keyword.
Here are two examples:
function anotherFunc<T extends { age: number }>
function someFunc<T, K extends keyof T>
What is the ??
operator?
The ??
is the nullish coalescing operator. This operator checks if the value on the left-hand side is nullish (either null
or undefined
), and if it is, it returns the value on the right-hand side.
The options parameter pattern in TypeScript allows us to pack optional parameters into a single options object, which simplifies the function signature and makes it easier to add or remove optional parameters in the future. Optional object properties are automatically unioned with undefined, which means that they can be absent from the object. Finally, we can use the nullish coalescing operator (??) to provide default values for these properties.
interface RectangleOptions { unit?: string; } function calculateRectangleArea( width: number, height: number, options: RectangleOptions = {} ): string { const unit = options.unit ?? "sq. units"; const area = width * height; return `${area} ${unit}`; }
When using a try/catch
block, how should we type the error passed to the catch
block?
We should catch the exception using an unknown
type. We need to do this because technically in JavaScript you can throw any type and it is a type safe alternative to any
. Within the catch block we must then use type guards (instanceof
works well here) to narrow the error’s type and then handle the error appropriately. If we cannot narrow the error’s type, rethrow the error.
<K extends keyof T>
The above code can be read as…
K extends keyof T
means that the type of K
(K
being a generic type variable) must be a subset of the keys of T
.
What is the syntax for Promise types in TypeScript?
When you are defining a promise type in TypeScript you use Promise
and then provide the type that the promise resolves to.
Some examples of how to properly define promise types:
Promise<string>
Promise<number
Promise<T>
function someFunc<T extends { age: number }>
The above code is an example of defining a generic constraint and can be read as…
T
is a subset of { age: number }
What is Pick
?
Pick<T, K extends keyof T>
is a utility type in TypeScript that allows us to create a new type from an existing type by specifying specific properties of T
that we want our new type to be composed of. If you specify a property that is not in T
there will be a type error.
What is Omit
?
Omit<T, K extends keyof any>
is a utility type in TypeScript that allows us to create a new type from an existing type by specifying the specific properties from T
that we do not want included in the new type. If you specify a property that is not in T
no type error will be given.
What is ReturnType<T>
?
ReturnType
is a TypeScript utility type that extracts the return type of a function (type).
What is the TypeScript operator typeof
?
The TypeScript typeof
operator returns the complete type of the given value that is inferred by the TypeScript compiler
What is Parameters<T>
?
Parameters<T>
is a TypeScript utility type that returns a tuple of the parameter types of a function.
Given the following code, what will the type of SumParameters
be equivalent to?
function sum(prefix: string, ...numbers: number[]): string {
const total = numbers.reduce((total, n) => total + n, 0);
return
${prefix}${total};
}
type SumParameters = Parameters<typeof sum>;
Parameters<typeof sum>
evaluates to a tuple type that is equivalent to [string, ...number[]]
.
What is Partial<T>
?
Partial<T>
is a TypeScript utility type that creates a new type based on an existing type, where all of the type’s properties are optional.