Skip to content

Conversation

Andarist
Copy link
Owner

No description provided.

getAlbumDetails(album, "producer");
```

`keyof` is an important building block when creating new types from existing types. We'll see later how we can use it with `as const` to build our own type-safe enums.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be good to call out those 2 gotchas:

const oops: keyof { concrete: "foo"; [k: string]: unknown } = "whatever";
const test: keyof { [k: string]: unknown } = 42;

// ^?
```

This is a more concise way to achieve the same result.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you could mention here that the index type gets distributed over the object type in this case


This solution makes the test pass, but it doesn't scale well. If the `programModes` array were to change, we would need to update the `AllPrograms` type manually.

Instead, we can use the `number` type as the argument to the indexed access type to represent all possible index values:
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's worth mentioning that it works like that because tuples are... arrays. We can have fixed-size tuples like:

type Tup = [string, number]

So one could imagine that we could only access this with 0 or 1 but to make tuples more practical they also have a numeric index signature that is a union of all possible property types here. And since a numeric index signature is there (just like on array types) it's permitted to use number to index into them.

This introduces unsoundness in scenarios like this:

function test(tup: [string, number], index: number) {
  const result = tup[index]
  //    ^?
}

test(['foo', 42], 5);

This could be seen as odd, the index signature isn't used when TS knows the exact number we are using for indexing... but as soon as we just introduce a little bit of indirection to lose the literal type it becomes fine with it:

const tup = ['foo', 42] as const
tup[5] // error

let num = 5
tup[num] // ok

It's a side-step from the type theory with the aim to make the language more approachable and useful and to allow for common patterns (even if they sometimes might bite back)

```typescript
type Price = SellAlbumParams[1]; // number
```

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

an extra example here could also who how useful it might be when building layers of functions:

function inner(a: string, b: number) {}

function outer(extra: boolean, ...args: Parameters<typeof inner>) {}

outer;
// ^?

type Artist = Exclude<Album["artist"], null | undefined>;
```

But `NonNullable` is more explicit and easier to read.
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might be good to call out that NonNullable today is defined as T & {} and that's because {} means exactly that: a non nullable value, something on which you can access properties. 'foo'.whatever while weird and a type error on its own... it's a not a runtime error, you simply get undefined back from this. The same can't be said about (null).whatever

```typescript
type Example = "a" | "b" | 1 | 2 | true | false;

type Strings = Extract<Example, string>;
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a powerful way to do such filtering (especially on generic level) is to use... intersections :) this could be a section of its own in this whole deriving types chapter. The example above can be simply rewritten as follows:

type Strings = Example & string;

@Andarist Andarist marked this pull request as ready for review July 15, 2024 12:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant