Skip to content

Latest commit

 

History

History
257 lines (188 loc) · 9.66 KB

types-as-sets.md

File metadata and controls

257 lines (188 loc) · 9.66 KB

Item 7: Think of Types as Sets of Values

Things to Remember

  • Think of types as sets of values (the type's domain). These sets can either be finite (e.g., boolean or literal types) or infinite (e.g., number or string).
  • TypeScript types form intersecting sets (a Venn diagram) rather than a strict hierarchy. Two types can overlap without either being a subtype of the other.
  • Remember that an object can still belong to a type even if it has additional properties that were not mentioned in the type declaration.
  • Type operations apply to a set's domain. The domain of A | B is the union of the domains of A and B.
  • Think of "extends," "assignable to," and "subtype of" as synonyms for "subset of."

Code Samples

const x: never = 12;
//    ~ Type 'number' is not assignable to type 'never'.

💻 playground


type A = 'A';
type B = 'B';
type Twelve = 12;

💻 playground


type AB = 'A' | 'B';
type AB12 = 'A' | 'B' | 12;

💻 playground


const a: AB = 'A';  // OK, value 'A' is a member of the set {'A', 'B'}
const c: AB = 'C';
//    ~ Type '"C"' is not assignable to type 'AB'

💻 playground


// OK, {"A", "B"} is a subset of {"A", "B"}:
const ab: AB = Math.random() < 0.5 ? 'A' : 'B';
const ab12: AB12 = ab;  // OK, {"A", "B"} is a subset of {"A", "B", 12}

declare let twelve: AB12;
const back: AB = twelve;
//    ~~~~ Type 'AB12' is not assignable to type 'AB'
//           Type '12' is not assignable to type 'AB'

💻 playground


type Int = 1 | 2 | 3 | 4 | 5 // | ...

💻 playground


interface Identified {
  id: string;
}

💻 playground


interface Person {
  name: string;
}
interface Lifespan {
  birth: Date;
  death?: Date;
}
type PersonSpan = Person & Lifespan;

💻 playground


const ps: PersonSpan = {
  name: 'Alan Turing',
  birth: new Date('1912/06/23'),
  death: new Date('1954/06/07'),
};  // OK

💻 playground


type K = keyof (Person | Lifespan);
//   ^? type K = never

💻 playground


interface Person {
  name: string;
}
interface PersonSpan extends Person {
  birth: Date;
  death?: Date;
}

💻 playground


interface NullyStudent {
  name: string;
  ageYears: number | null;
}
interface Student extends NullyStudent {
  ageYears: number;
}

💻 playground


interface StringyStudent extends NullyStudent {
  //      ~~~~~~~~~~~~~~
  // Interface 'StringyStudent' incorrectly extends interface 'NullyStudent'.
  ageYears: number | string;
}

💻 playground


interface Vector1D { x: number; }
interface Vector2D extends Vector1D { y: number; }
interface Vector3D extends Vector2D { z: number; }

💻 playground


interface Vector1D { x: number; }
interface Vector2D { x: number; y: number; }
interface Vector3D { x: number; y: number; z: number; }

💻 playground


function getKey<K extends string>(val: any, key: K) {
  // ...
}

💻 playground


getKey({}, 'x');  // OK, 'x' extends string
getKey({}, Math.random() < 0.5 ? 'a' : 'b');  // OK, 'a'|'b' extends string
getKey({}, document.title);  // OK, string extends string
getKey({}, 12);
//         ~~ Type 'number' is not assignable to parameter of type 'string'

💻 playground


const list = [1, 2];
//    ^? const list: number[]
const tuple: [number, number] = list;
//    ~~~~~ Type 'number[]' is not assignable to type '[number, number]'
//          Target requires 2 element(s) but source may have fewer

💻 playground


const triple: [number, number, number] = [1, 2, 3];
const double: [number, number] = triple;
//    ~~~~~~ '[number, number, number]' is not assignable to '[number, number]'
//           Source has 3 element(s) but target allows only 2.

💻 playground


type T = Exclude<string|Date, string|number>;
//   ^? type T = Date
type NonZeroNums = Exclude<number, 0>;
//   ^? type NonZeroNums = number

💻 playground


interface Lockbox {
  code: number;
}
interface ReadonlyLockbox {
  readonly code: number;
}

💻 playground


const box: Lockbox = { code: 4216 };
const robox: ReadonlyLockbox = { code: 3625 };
box.code = 1234;  // ok
robox.code = 1234;
//    ~~~~ Cannot assign to 'code' because it is a read-only property.

💻 playground