2

I'm a beginner in TypeScript, and I'm not sure why TS is complaining on the left hand side of the = sign.

const nonMandatoryFields = [
    'address',
    'mobilePhone',
    'postCode',
    'state',
    'suburb',
    'testMode',
];

interface XeroClientData {
    address: string;
    firstName: string;
    lastName: string;
    suburb: string;
    postCode: string;
    state: string;
    country: string;
    mobilePhone: string;
    email: string;
    recordId: string;
    testMode: boolean;
}

const sanitiseFields = (fields: XeroClientData) => {
    for (const key of nonMandatoryFields) {
        // Error on the left hand side
        // Element implicitly has an 'any' type 
        // because expression of type 'string' can't be used to index type 'XeroClientData'
        fields[key] = fields[key as keyof XeroClientData]
            ? fields[key as keyof XeroClientData]
            : '';
    }

    return fields;
};

As you can see, I've tried the keyof operator and it worked fine on the right side.

If somebody could please point me to a reference doc or point out the problem - would greatly appreciate it. Thanks!


EDIT: Have managed to fix the issue thanks to @jcalz (BIG THANKS!). See below:

const nonMandatoryFields = [
    'address',
    'mobilePhone',
    'postCode',
    'state',
    'suburb',
] as const;

type NonMandatoryField = typeof nonMandatoryFields[number];

interface XeroClientData {
    address: string;
    firstName: string;
    lastName: string;
    suburb: string;
    postCode: string;
    state: string;
    country: string;
    mobilePhone: string;
    email: string;
    recordId: string;
    testMode?: boolean; // optional property
}

const sanitiseFields = (fields: XeroClientData) => {
    nonMandatoryFields.forEach(<K extends NonMandatoryField>(key: K) => {
        fields[key] = fields[key] ? fields[key] : '';
    });
    return fields;
};
9
  • Can you share a playground link with a little more of code please? Commented Dec 5, 2022 at 14:40
  • Please provide a self-contained minimal reproducible example that demonstrates your issue when pasted as-is into a standalone IDE. Right now I get errors almost certainly unrelated to your question; my IDE has no idea what SomeInterface and noMandatoryFields are, so it's hard to know how to advise you. If you do make such an edit and want me to take another look, please mention @jcalz in a comment so that I'm notified. Commented Dec 5, 2022 at 17:28
  • @jcalz Thank you so much - apologies I'm still learning how to navigate SO. I've updated the above to include concrete values. Commented Dec 5, 2022 at 20:40
  • 1
    Well, in trying to fix it (see this playground link) I hit the issue where you are possibly assigning "" to a boolean property. That's an error for a good reason. What are you actually trying to do there? Commented Dec 5, 2022 at 20:47
  • 1
    You're new to SO, right? If you want to answer your own question you can, but it should be as an answer post and not as an edit to the question. But I am ready to post an answer which is essentially just doing the const assertion; you don't need to refactor to a generic callback. Commented Dec 6, 2022 at 0:58

1 Answer 1

1

I am assuming your nonMandatoryFields all accept string types (so the default value of "" is appropriate) like this:

const nonMandatoryFields = [
  'address',
  'mobilePhone',
  'postCode',
  'state',
  'suburb',
];

If so, then you need to make sure that the compiler keeps track of the literal types of the strings in that array. Right now the compiler infers that nonMandatoryFields is of type string[], which is too wide for it to be happy with fields[key] (because key could be any string for all it knows).

The easiest way to do this is to use a const assertion on the initializer, like this:

const nonMandatoryFields = [
  'address',
  'mobilePhone',
  'postCode',
  'state',
  'suburb',
] as const;

Now the type of nonMandatoryFields is

// const nonMandatoryFields: readonly ["address", "mobilePhone", 
//   "postCode", "state", "suburb"];

so the compiler knows exactly which values are present (and also their locations within the array, although this is not needed for your code to work).

Once you do this, everything starts working:

const sanitiseFields = (fields: XeroClientData) => {
  for (const key of nonMandatoryFields) {
    fields[key] = fields[key] ? fields[key] : ''; // okay
  }
  return fields;
};

The compiler knows that each key is a key of XeroClientData which accepts a string. Note that you can simplify x ? x : y to x || y (unless evaluating x twice somehow affect things), so you can rewrite this as

const sanitiseFields = (fields: XeroClientData) => {
  for (const key of nonMandatoryFields) {
    fields[key] = fields[key] || ''; // okay
  }
  return fields;
};

Playground link to code

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.