Skip to content

Latest commit

 

History

History
172 lines (123 loc) · 6.65 KB

prefer-declarations-to-assertions.md

File metadata and controls

172 lines (123 loc) · 6.65 KB

Item 9: Prefer Type Annotations to Type Assertions

Things to Remember

  • Prefer type annotations (: Type) to type assertions (as Type).
  • Know how to annotate the return type of an arrow function.
  • Use type assertions and non-null assertions only when you know something about types that TypeScript does not.
  • When you use a type assertion, include a comment explaining why it's valid.

Code Samples

interface Person { name: string };

const alice: Person = { name: 'Alice' };
//    ^? const alice: Person
const bob = { name: 'Bob' } as Person;
//    ^? const bob: Person

💻 playground


const alice: Person = {};
//    ~~~~~ Property 'name' is missing in type '{}' but required in type 'Person'
const bob = {} as Person;  // No error

💻 playground


const alice: Person = {
  name: 'Alice',
  occupation: 'TypeScript developer'
// ~~~~~~~~~ Object literal may only specify known properties,
//           and 'occupation' does not exist in type 'Person'
};
const bob = {
  name: 'Bob',
  occupation: 'JavaScript developer'
} as Person;  // No error

💻 playground


const people = ['alice', 'bob', 'jan'].map(name => ({name}));
// { name: string; }[]... but we want Person[]

💻 playground


const people = ['alice', 'bob', 'jan'].map(
  name => ({name} as Person)
); // Type is Person[]

💻 playground


const people = ['alice', 'bob', 'jan'].map(name => ({} as Person));
// No error

💻 playground


const people = ['alice', 'bob', 'jan'].map(name => {
  const person: Person = {name};
  return person
}); // Type is Person[]

💻 playground


const people = ['alice', 'bob', 'jan'].map(
  (name): Person => ({name})
); // Type is Person[]

💻 playground


const people: Person[] = ['alice', 'bob', 'jan'].map(name => ({name})); // OK

💻 playground


document.querySelector('#myButton')?.addEventListener('click', e => {
  e.currentTarget
  // ^? (property) Event.currentTarget: EventTarget | null
  // currentTarget is #myButton is a button element
  const button = e.currentTarget as HTMLButtonElement;
  //    ^? const button: HTMLButtonElement
});

💻 playground


const elNull = document.getElementById('foo');
//    ^? const elNull: HTMLElement | null
const el = document.getElementById('foo') as HTMLElement;
//    ^? const el: HTMLElement

💻 playground


const el = document.getElementById('foo')!;
//    ^? const el: HTMLElement

💻 playground


document.getElementById('foo')?.addEventListener('click', () => {
  alert('Hi there!');
});

💻 playground


interface Person { name: string; }
const body = document.body;
const el = body as Person;
//         ~~~~~~~~~~~~~~
// Conversion of type 'HTMLElement' to type 'Person' may be a mistake because
// neither type sufficiently overlaps with the other. If this was intentional,
// convert the expression to 'unknown' first.

💻 playground


const el = document.body as unknown as Person;  // OK

💻 playground