Search code examples
node.jstypescripttypescript-types

In Typescript, can I use an Interface or Type to reject bad input data?


I want to load in some text from a CSV file with 3 fields: The AWS resource name, the AWS resource type, and an action. The resource name can be any ARN, and the resource type is one of "load_balancer", "ecs_cluster", or "lambda_function". The action can be one of REMOVE or ADD. Each CSV line is called a ResourceEntry in this post. I'm not sure of the correct terminology to use, I am very new to Typescript.

When reading in the records from CSV, I'd like to reject invalid records, for instance an unknown action, or a resource type that isn't listed above. Can I accomplish that with an Interface defining the ResourceEntry type?

Here's what I'm doing right now:

// also has a 'class IterableResourceEntry implements ResourceEntry {'
interface ResourceEntry {
  resource_name: string;
  resource_type: ResourceType;
  action: ActionType;
}

interface ActionType {
  type: 'REMOVE' | 'ADD';
}

interface ResourceType {
  type: 'load_balancer' | 'ecs_cluster' | 'lambda_function';
}

Is it possible to use those definitions to validate each record?


Solution

  • Yes, you can have a function that checks if a given value is a ResourceEntry (although your definitions of ActionType and ResourceType are slightly off.

    const actionTypes = ['REMOVE', 'ADD'] as const;
    const resourceTypes = ['load_balancer', 'ecs_cluster', 'lambda_function'] as const;
    
    type ActionType = typeof actionTypes[number];
    type ResourceType = typeof resourceTypes[number];
    
    interface ResourceEntry {
      resource_name: string;
      resource_type: ResourceType;
      action: ActionType;
    }
    
    function isString(obj: any): obj is string {
      return typeof obj === 'string' || obj instanceof String;
    }
    
    function isResourceEntry(obj: any): obj is ResourceEntry {
        return isString(obj.resource_name) && 
          actionTypes.includes(obj.action) && 
          resourceTypes.includes(obj.resource_type);
    }