Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which alternative for type "object" could be in TypeScript?

Tags:

typescript

The rule ban-types of newest @typescript-eslint/ban-types disallows object type as default. I need to refactor my type analyzing functions according this rule.

I understand that TypeScript-ESLint is not source of truth, but wherever I follow to ban-types or violate it, I need to comprehend my decision.

function isNonNullObject(potentialObject: unknown): potentialObject is object {
  return typeof potentialObject === "object" && potentialObject !== null;
}

function isNonEmptyObject(potentialObject: unknown): potentialObject is object {
  if (typeof potentialObject !== "object" || potentialObject === null) {
    return false;
  }
  return Object.entries(potentialObject as {[key: string]: unknown}).length > 0;
}


function isEmptyObject(potentialObject: unknown): potentialObject is object {
  if (typeof potentialObject !== "object" || potentialObject === null) {
    return false;
  }
  return Object.entries(potentialObject as {[key: string]: unknown}).length === 0;
}

The basic usage of this function external data analysis (from API or files):

if (isNonNullObject(data)) {
  throw new Error("The data is invalid; object expected.");
}

Should I replace object to other type in this case, or exclusively here object is allowable?

Usage example: data fetching

In real projects, data analyzing functionality is being wrapped to special utility, but it's the concept as below:

type ValidResponseData = {
    products: Array<Products>;
    productsCount: number;
};


@Component
class ProductsListPage extends Vue {

  private products: Array<Products> = [];
  private productsCount: number = 0;

  private async created(): Promise<void> {

    try {

      // We must not trust to external data, so it's 'unknown'
      const responseData: unknown = await ProductFetchingAPI.fetchAllProducts();

      if (!isNonNullObject(responseData)) {
       throw new Error(
         `The response data data is invalid: non-null object expected, real type: ${typeof responseData},` +
         `, value: ${responseData}.`
       );
      }

      // Below checks are meaningless if "responseData" is not object.
      if (!Object.prototype.hasOwnProperty.call(responseData, "products")) {
        throw new Error(
          "Expected that response data has 'products' property but it's missing".
        );
      }
      if (!Object.prototype.hasOwnProperty.call(responseData, "productsCount")) {
         throw new Error(
           "Expected that response data has 'productsCount' property but it's missing".
         );
      }

      // 'products' and 'productsCount' analysis ....
      const validResponseData: ValidResponseData = responseData as ValidResponseData;

      this.products = validResponseData.products;
      this.productsCount = validResponseData.productsCount;

    } catch (error) {
      NotificationBarService.displayNotificationBar({
        type: NotificationBarService.NotificationsTypes.error,
        originalError: error,
        text: "Failed to fetch products."
      });           
    }
  }
}

Usage example: data analyze from file

const rawData: unknown = /* parse data from the file by appropriate library ... */;

if (!isNonNullObject(rawData)) {
 throw new Error(
   `The file content is invalid: non-null object expected, real type: ${typeof rawData},` +
     `, value: ${rawData}.`
 );
}

// Without isNonNullObject(rawData), we can not execute below loop
for (const value of Object.entires(rawData)) {
    // check each property
}
like image 983
Takeshi Tokugawa YD Avatar asked Jun 13 '20 09:06

Takeshi Tokugawa YD


People also ask

What is object type in typescript?

TypeScript object type is type of any non-primitive values. TypeScript has introduced new type called as object with version 2.2. It represents all non-primitive types. There are some Primitive types such as string, number, bigint, null, boolean, symbol, undefined. All other types are considered to be non-primitive types.

What are primitive types in typescript?

Introduction to TypeScript object type. The TypeScript object type represents all values that are not in primitive types. The following are primitive types in TypeScript: number. bigint. string. boolean. null.

What is an anonymous type in typescript?

In TypeScript, we represent those through object types. As we’ve seen, they can be anonymous: or a type alias. In all three examples above, we’ve written functions that take objects that contain the property name (which must be a string) and age (which must be a number ).

How do you extend an interface in typescript?

interface s can also extend from multiple types. interface s allowed us to build up new types from other types by extending them. TypeScript provides another construct called intersection types that is mainly used to combine existing object types. An intersection type is defined using the & operator.


Video Answer


2 Answers

Rather than isNonNullObject, it should be sufficient to simply check:

// the `ban-types` rule should allow `Object` here because it's
// the value `Object`, not the type `Object`. Note the capital "O".
if (!(responseData instanceof Object)) {
  throw new Error(...);
}

// `responseData`'s type is now narrowed to `Object`, so you can now call `hasOwnProperty`
if (!responseData.hasOwnProperty("products")) {
 throw new Error(...);
}

...

This article also might serve as a nice refresher: https://mariusschulz.com/blog/the-object-type-in-typescript

like image 109
lazytype Avatar answered Oct 06 '22 01:10

lazytype


As for the replacement type for object: Record<string, unknown> is a good start - basically it is saying the data is an object with string properties of unknown values.

As for the type checks - I recently made a TypeScript transformer that can create type guards from your types automatically, it is called ts-type-checked and it is available on NPM, type-check it out! :D

With ts-type-checked you no longer need to manually check whether an unknown object matches a certain type (or interface), you can just write:

import { isA } from 'ts-type-checked';

// ...

if (isA<ValidResponseData>(value) {
  // ...
}
like image 44
Ján Jakub Naništa Avatar answered Oct 06 '22 01:10

Ján Jakub Naništa