Skip to content

Type Annotation for JavaScript Objects

Derek Lewis edited this page Oct 16, 2022 · 5 revisions

TypeScript Type Annotation for JavaScript Objects

⚠︎ Anti-pattern:
Use of a banned type detected JS-0296

Description

As some standard built-in JavaScript objects are considered dangerous or harmful, some built-in types have aliases.
The practice of banning certain types is often a good idea to help improve consistency and safety.

Applies to: TypeScript
Reported by: TSLint

This rule bans specific types from being used. Does not ban the corresponding runtime objects from being used.
It takes a list of ["regex", "optional explanation here"], which bans types that match regex.

This rule includes a set of "best practices" intended to provide safety and standardization in our codebase:

  • Don't use the upper-case primitive types; lower-case types should be used for consistency:
    • Object -> object
    • Number -> number
    • String -> string
    • BigInt -> bigint
    • Symbol -> symbol
    • Boolean -> boolean

ⓧ Avoid the object type if able, as it's currently hard to use due to not being able to assert that keys exist.

Purposely select the lowercase object type instead of the capitalized Object type when:

  • The variable in question being annotated doesn't inherit from Object.prototype^1

ⓘ  Better still would be to define the object's shape, but if it can be anything
ⓘ  object is better than any

ⓘ  unknown is better than any
⚠︎ You can assign any values to unknown type variables, but you cannot use them before doing a type check or type assertion.


⚠︎ Don't use Object as a type. Maybe use {} instead? Object literal expressions should be populated, though.


⚠︎ Avoid the both Object and {} types, as they mean "any non-nullish value".
ⓘ  This is a point of confusion for many developers, who think it means "any object type".


  • Avoid the Function type, as it provides little safety for the following reasons:
    • It provides no type safety when calling the value, which means it's easy to provide the wrong arguments
    • It accepts class declarations, which will fail when called, as they are called without the new keyword

Examples

Bad Practice

// use of upper-case primitives
const str: String = "foo";
const bool: Boolean = true;
const num: Number = 1;
const symb: Symbol = Symbol("foo");

// use a proper function type
const func: Function = () => 1;

// use safer object types
const lowerObj: object = {};
const capitalObj1: Object = 1;
const capitalObj2: Object = { a: "string" };
const curly1: {} = 1;
const curly2: {} = { a: "string" };

Recommended

// use lower-case primitives for consistency
const str: string = "foo";
const bool: boolean = true;
const num: number = 1;
const symb: symbol = Symbol("foo");

// use a proper function type
const func: () => number = () => 1;

// use safer object types
const lowerObj: Record<string, unknown> = {};

const capitalObj1: number = 1;
const capitalObj2: { a: string } = { a: "string" };

const curly1: number = 1;
const curly2: Record<"a", string> = { a: "string" };
Clone this wiki locally