Skip to content

Latest commit

 

History

History
177 lines (129 loc) · 8.98 KB

File metadata and controls

177 lines (129 loc) · 8.98 KB

Item 20: Understand How a Variable Gets Its Type

Things to Remember

  • Understand how TypeScript infers a type from a literal by widening it.
  • Familiarize yourself with the ways you can affect this behavior: const, type annotations, context, helper functions, as const, and satisfies.

Code Samples

interface Vector3 { x: number; y: number; z: number; }
function getComponent(vector: Vector3, axis: 'x' | 'y' | 'z') {
  return vector[axis];
}

💻 playground


let x = 'x';
let vec = {x: 10, y: 20, z: 30};
getComponent(vec, x);
//                ~ Argument of type 'string' is not assignable
//                  to parameter of type '"x" | "y" | "z"'

💻 playground


const mixed = ['x', 1];

💻 playground


let x = 'x';
x = 'a';
x = 'Four score and seven years ago...';

💻 playground


const x = 'x';
//    ^? const x: "x"
let vec = {x: 10, y: 20, z: 30};
getComponent(vec, x);  // OK

💻 playground


const obj = {
  x: 1,
};
obj.x = 3;  // OK
obj.x = '3';
//  ~ Type 'string' is not assignable to type 'number'
obj.y = 4;
//  ~ Property 'y' does not exist on type '{ x: number; }'
obj.z = 5;
//  ~ Property 'z' does not exist on type '{ x: number; }'
obj.name = 'Pythagoras';
//  ~~~~ Property 'name' does not exist on type '{ x: number; }'

💻 playground


const obj: { x: string | number } = { x: 1 };
//    ^? const obj: { x: string | number; }

💻 playground


const obj1 = { x: 1, y: 2 };
//    ^? const obj1: { x: number; y: number; }

const obj2 = { x: 1 as const, y: 2 };
//    ^? const obj2: { x: 1; y: number; }

const obj3 = { x: 1, y: 2 } as const;
//    ^? const obj3: { readonly x: 1; readonly y: 2; }

💻 playground


const arr1 = [1, 2, 3];
//    ^? const arr1: number[]
const arr2 = [1, 2, 3] as const;
//    ^? const arr2: readonly [1, 2, 3]

💻 playground


function tuple<T extends unknown[]>(...elements: T) { return elements; }

const arr3 = tuple(1, 2, 3);
//    ^? const arr3: [number, number, number]
const mix = tuple(4, 'five', true);
//    ^? const mix: [number, string, boolean]

💻 playground


const frozenArray = Object.freeze([1, 2, 3]);
//    ^? const frozenArray: readonly number[]
const frozenObj = Object.freeze({x: 1, y: 2});
//    ^? const frozenObj: Readonly<{ x: 1; y: 2; }>

💻 playground


type Point = [number, number];
const capitals1 = { ny: [-73.7562, 42.6526], ca: [-121.4944, 38.5816] };
//    ^? const capitals1: { ny: number[]; ca: number[]; }

const capitals2 = {
  ny: [-73.7562, 42.6526], ca: [-121.4944, 38.5816]
} satisfies Record<string, Point>;
capitals2
// ^? const capitals2: { ny: [number, number]; ca: [number, number]; }

💻 playground


const capitals3: Record<string, Point> = capitals2;
capitals3.pr;  // undefined at runtime
//        ^? Point
capitals2.pr;
//        ~~ Property 'pr' does not exist on type '{ ny: ...; ca: ...; }'

💻 playground


const capitalsBad = {
    ny: [-73.7562, 42.6526, 148],
//  ~~ Type '[number, number, number]' is not assignable to type 'Point'.
    ca: [-121.4944, 38.5816, 26],
//  ~~ Type '[number, number, number]' is not assignable to type 'Point'.
} satisfies Record<string, Point>;

💻 playground