Basics - everyday types Flashcards

1
Q

List TypeScript primitive types

A

Same as JavaScript primititve types: string, number, bigint, undefined, null, boolean and symbol

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

How do you define an Array type in TypeScript?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Explain TypeScript special type any?

A

Special type any can be used whenever you don’t want a particular value to cause typechecking errors.

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Why should you NOT use any type?

A

any type is not type-checked. This means that TypeScript will consider valid a variable with any type. No matter where it is used.

What’s worst is that the use of any type is “contagious” in that it can quitely spread throughout a codebase removing the type safety that TypeScript provieds. For example:

function f1() {
  const x: any = expressionReturningFoo(); // Don’t do this
  processBar(x);
  return x
}

function g() {
  const foo = f1(); // Type is any
  foo.fooMethod(); // This call is unchecked!
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

What type should you use instead of any?

A

unknown type

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Explain TypeScript special type unknown

A

The unknown type represents any value. This is similar to the any type, but is safer because it’s not legal to do anything with an unknown value:

function f1(a: any) {
  a.b(); // OK
}
function f2(a: unknown) {
  a.b(); // NOT OK: Object is of type 'unknown'.
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

How do you define a type anotation?

A

:type after the name of the variable, parameter or property. There can be a space after the colon.

let myName: string = "Alice";
function(foo: string, bar: number) {}

Note: TypeScript doesn’t use “types on the left”-style declarations like int x = 0; Type annotations will always go after the thing being typed.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

How do you define the return type of a declared function?

A

Adding the return type after the parameter list with a colon :

function getFavoriteNumber(): number {
  return 26;
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

How do you define an object type?

A

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:

// The parameter's type annotation is an object type
function 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 });
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

How do you define an optional property on an object type?

A

Adding a ? after the property name:

function printName(obj: { first: string; last?: string }) {
  // ...
}
// Both OK
printName({ first: "Bob" });
printName({ first: "Alice", last: "Alisson" });
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

What is a union type?

A

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.

Example:

function printId(id: number | string) {
  console.log("Your ID is: " + id);
}

printId(101); // OK
printId("202"); // OK
printId({ myID: 22342 });  // NOT OK
// Throws Error `Argument of type '{ myID: number; }' is not assignable  to parameter of type 'string | number'`.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

How can you narrow the type of a union type?

A

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”:

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);
  }
}

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

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);
  }
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

What are Type Aliases?

A

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

type 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);
}

You can actually use a type alias to give a name to any type at all. Primitive types, objects, union types, etc.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

What is an Interface Declaration?

A

An interface declaration is one of the ways you can name an object type in TypeScript (type aliases are another way)

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);
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

What are the differences between Type Aliases and Interfaces?

A
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

What is a type assertion?

A

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:

const myCanvas = document.getElementById("main_canvas") as HTMLCanvasElement;

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

const myCanvas = <HTMLCanvasElement>document.getElementById("main_canvas");
17
Q

Why does TypeScript only allows type assertions which convert to a more specific or less specific version of a type?

A

To prevent “impossible” coercions like:

const x = "hello" as number; // Error: 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.

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 unknown or any, then to the desired type:

const a = (expr as unknown) as T;
18
Q

What are literal types?

A

Literal types refer to specific strings and numbers in type positions.

let x: "hello" = "hello"; // OK
x = "hello";  // OK
x = "howdy"; // Error: Type '"howdy"' is not assignable to type '"hello"'.
19
Q

What is literal inference

A

When you initialize a variable with an object, TypeScript assumes that the properties of that object might change values later.

For example:

const req = { url: "https://example.com", method: "GET" };
handleRequest(req.url, req.method); // Error: Argument of type 'string' is not assignable to parameter of type '"GET" | "POST"'.

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 2
handleRequest(req.url, req.method as "GET");
  1. 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);

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.

20
Q

How does null and undefined behave when compiler option strictNullChecks is off?

A

With 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.

21
Q

How does null and undefined behave when compiler option strictNullChecks is on?

A

With 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:

function doSomething(x: string | null) {
  if (x === null) {
    // do nothing
  } else {
    console.log("Hello, " + x.toUpperCase());
  }
}
22
Q

What does the Non-null Assertion Operator (Postfix !) do?

A

TypeScript 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:

function liveDangerously(x?: number | null) {
  // No error
  console.log(x!.toFixed());
}

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.

23
Q

What is the BigInt primitive

A

bigint (all lowercase) is a new type of primitive. You can get a bigint by calling the BigInt() function or by writing out a BigInt literal by adding an n to the end of any integer numeric literal:

let foo: bigint = BigInt(100); // the BigInt function
let bar: bigint = 100n; // a BigInt literal

number and bigint, are separate domains. As specified in ECMAScript, mixing numbers and bigints in arithmetic operations is an error. You’ll have to explicitly convert values to BigInts.

Also important to note is that bigints produce a new string when using the typeof operator: the string "bigint". Thus, TypeScript correctly narrows using typeof as you’d expect.

24
Q

What is the Symbol primitive

A

symbol (all lowercase) is a new primitive data type, just like number and string.
symbol values are created by calling the Symbol constructor.

let sym1 = Symbol();
let sym2 = Symbol("key"); // optional string key

Symbols are immutable, and unique.

let sym2 = Symbol("key");
let sym3 = Symbol("key");
sym2 === sym3; // false, symbols are unique

Just like strings, symbols can be used as keys for object properties.

const sym = Symbol();
let obj = {
  [sym]: "value",
};
console.log(obj[sym]); // "value"

Also important to note is that symbols produce a new string when using the runtime typeof operator: the string "symbol". Thus, TypeScript correctly narrows using typeof as you’d expect.

25
Q

What is a unique symbol?

A

unique symbol is a subtype of symbol, and are produced only from calling Symbol() or Symbol.for(), or from explicit type annotations. This type is only allowed on const declarations and readonly static properties.

In order to reference a specific unique symbol, you’ll have to use the typeof type operator. Each reference to a unique symbol implies a completely unique identity that’s tied to a given declaration.

declare const sym1: unique symbol;
 
// sym2 can only be a constant reference.
let sym2: unique symbol = Symbol(); // Error: A variable whose type is a 'unique symbol' type must be 'const'.
 
// Works - refers to a unique symbol, but its identity is tied to 'sym1'.
let sym3: typeof sym1 = sym1;
 
// Also works.
class C {
  static readonly StaticSymbol: unique symbol = Symbol();
}

Note: unique symbol is a subtype of symbol type. It allows you to replicate the behaviour of string literals with symbols.