Wei-jye
Wei-jye

Reputation: 442

Typescript Definition - How to define an array property in object that accept certain strings

I need to define an object that has an array of strings, but the string can only accept certain values. The examples below are the possible scenario:

let user = {
   name: 'John',
   communicationPreferences: ['email', 'whatsApp']
}

or

let user = {
   name: 'John',
   communicationPreferences: ['whatsApp', 'weChat', 'skype']
}

or

 let user = {
   name: 'John',
   communicationPreferences: ['email', 'whatsApp', 'weChat', 'skype', 'line', 'sms']
 }

Upvotes: 1

Views: 138

Answers (4)

Greg Rozmarynowycz
Greg Rozmarynowycz

Reputation: 2085

You have a some options depending on your exact situation:

Enum: Provides flexible, type-safe definition of your values that allow for run-time look-ups and reverse look-ups. This is likely your preferred solution:

enum Channel {
    Email = 'email', // if you prefer email = 'email', etc. that's also very doable
    WhatsApp = 'whatsApp',
    WeChat = 'weChat',
    Skype = 'skype',
    Line = 'line',
    SMS = 'sms',
}

class User {
   constructor(private name: string, private communicationPreferences: Channel[]) { };
}

const john = new User('john', [Channel.SMS]); // {name: 'john', communicationPreferences: ['sms']);

// example of reverse look-up
const channel = 'skype'; // from some unverified source
if (Channel[channel] === undefined) {
   // handle error case at run-time
}

Union Type: If you wanna be quick and dirty about it, you can use a string literal union type. This would provide compile-time only safety, or if you have a very extreme case where you want to limit the (very small) overhead of an Enum:

type Channel = 'email'| 'whatsApp'| 'weChat' | 'skype' | 'line'| 'sms';

interface User {
   name: string;
   communicationPreferences: Channel[];
}

const john: User = {
    name: 'john',
    communicationPreferences: ['telegraph']; // would fail to compile
}

Index Type: Your last option would be combining the keyof and typeof operators to produce a dynamic union type. This might be useful if your communication channel options are coming from an external JSON/JS file, especially if it may change:

// Some example object you're getting

const channels = {
   skype: { ... },
   sms: { ... },
   line: { ... },
   // and so on
}

// in your script
type Channel = keyof typeof channels; // "skype" | "sms" | "line" | ...

In your situation, you might also be interested in the Set class; it provides a distinct (only one of each value allowed) collection:

// define Channel type, from options above

class User {
    private communicationPreferences: Set<Channel>;

    constructor(private name: string, channels: Channel[]) {
        this.communicatonPreferences = new Set<Channel>(channels);
    }

    public serialize() {
        return {
            name: this.name,
            communicationPreferences: Array.from(this.communicationPreferences)
        }
    }
}

Upvotes: 1

Asura
Asura

Reputation: 869

Typescript allow usage of Enum:

export enum communicationPreferences{
whatsApp=0
weChat=1
skype=2
}

var value: communicationPreferences=communicationPreferences[communicationPreferences.whatsApp]

beware of trying to use something like communicationPreferences.whatsApp by itself which returns the integer value.

seems like typescript 2.4 now support string-based enum so we dont even need to do the integer stuff and just declare whatsApp="whatsApp"

Upvotes: 0

meziantou
meziantou

Reputation: 21337

type CommunicationPreferences = 'email' | 'whatsApp' | 'weChat' | 'skype' | 'line' | 'sms';

interface User {
    name: string;
    communicationPreferences: CommunicationPreferences[];
}

let user : User = {
    name: 'John',
    communicationPreferences: ['email', 'whatsApp', 'weChat', 'skype', 'line', 'sms']
}

Upvotes: 0

Anthony Giretti
Anthony Giretti

Reputation: 327

To achieve this I would build a strongly Typed object using Enums like this:

export enum ChannelCommunication {
        whatsApp = 'whatsApp',
        weChat = 'weChat',
        skype = 'skype',
        email = 'email',
        line = 'line',
        sms = 'sms'
    }

    export class User {

        private _name: String;
        private _communicationPreferences: ChannelCommunication[];

        constructor(name:String, communicationPreferences: Array<ChannelCommunication>) {
            this._name = name;
            this._communicationPreferences = communicationPreferences;
        }
        public name: String;
        public communicationPreferences: ChannelCommunication[]
    }



    let communicationPreferences: ChannelCommunication[] = [ChannelCommunication.email,
 ChannelCommunication.line,
 ChannelCommunication.skype,
 ChannelCommunication.sms,
 ChannelCommunication.weChat,
 ChannelCommunication.whatsApp];

let user = new User('John',communicationPreferences);

Hope it answers your question

Upvotes: 0

Related Questions