typescript - typvakter för valfria parametrar


Jag har följande fixture-fil som jag har typkontrollerat nedan. Den har några få valfria egenskaper

fixturefil

 {
      "profiles": [
        {
          "name": "Laakea",
          "phoneNumber": "2033719225",
          "authGroupName": "Drivers"
        },
        {
          "name": "Lkhagvasuren",
          "phoneNumber": "2033719225",
          "authGroupName": "Drivers"
        },
        {
          "name": "Joaquin",
          "phoneNumber": "2033719225"
        }
      ]
    }

typgränssnitt

 export interface Profile {
      name: string;
      authGroupName?: string;
      phoneNumber?: string;
      email?: string;
    }

typvaktfunktion

export function isValidProfiles(profiles: unknown): profiles is Profile[] {
  if (!Array.isArray(profiles)) {
    return false;
  }
  for (let index = 0; index < profiles.length; index += 1) {
    if (typeof profiles[index].name !== 'string') {
      return false;
    }
    if (profiles[index].email) {
      if (typeof profiles[index].email !== 'string') {
        return false;
      }
    }
    if (profiles[index].phoneNumber) {
      if (typeof profiles[index].phoneNumber !== 'string') {
        return false;
      }
    }
    if (profiles[index].authGroupName) {
      if (typeof profiles[index].authGroupName !== 'string') {
        return false;
      }
    }
  }

  return true;
}

jag undrade om jag kunde skriva det bättre istället för alla dessa if-satser?

Svar #1

Med tanke på att du i princip har identisk kod för kontrollerna mot email, phoneNumber och authGroupName-egenskaperna, kan du omstrukturera dessa kontroller till en enda kodbit som körs flera gånger.

Till exempel kan du skapa en array av nycklarna (starkt typad via en const-påstående så att kompilatorn kommer ihåg att värdena i dem bokstavligen är "email", "phoneNumber" och "authGroupName" istället för bara string), och sedan använda dess every()-metod för att returnera true om och endast om kontrollen fungerar för varje nyckel, för varje medlem av profiles-arrayen:

function isValidProfiles(profiles: unknown): profiles is Profile[] {
  if (!Array.isArray(profiles)) { return false; }
  const keys = ["email", "phoneNumber", "authGroupName"] as const;
  return profiles.every(p => p && (typeof p === "object") && (typeof p.name === "string") &&
    keys.every(k => typeof p[k] === "undefined" || typeof p[k] === "string")
  );
}

Här använder jag kontrollen att, för varje nyckel k, och för varje profil p, är typeof p[k] antingen "undefined" eller "string". Detta hanterar korrekt string-egenskaper och saknade egenskaper. Det kommer också att acceptera en egenskap vars nyckel finns men vars värde uttryckligen är undefined. Det kanske inte är vad du anser vara "valfritt", men TypeScript gör det som standard... om du inte uttryckligen aktiverar kompilatoralternativet --exactOptionalPropertyTypes... vilket inte ens är en del av den "standard" --strict-sviten av kompilatorfunktioner.


Okej, låt oss testa det:

const val = [
  {
    "name": "Laakea",
    "phoneNumber": "2033719225",
    "authGroupName": "Drivers"
  },
  {
    "name": "Lkhagvasuren",
    "phoneNumber": "2033719225",
    "authGroupName": "Drivers"
  },
  {
    "name": "Joaquin",
    "phoneNumber": "2033719225"
  }
];
if (isValidProfiles(val)) {
  console.log(val.map(
    x => x.authGroupName ?? "no-auth").join(",")
  ); // "Drivers,Drivers,no-auth" 
} else {
  console.log("nope")
}

Ser bra ut. Kompilatorn validerar val som att det är en Profile[] och kan behandla det som sådant.

Lekplatslänk till kod

Svar #2

kom på det här -

export function isValidProfiles(profiles: unknown): profiles is Profile[] {
  if (!Array.isArray(profiles)) {
    return false;
  }
  for (let index = 0; index < profiles.length; index += 1) {
    if (
      !(
        typeof profiles[index].name === 'string' &&
        (!profiles[index].phoneNumber || typeof profiles[index].phoneNumber === 'string') &&
        (!profiles[index].email || typeof profiles[index].email === 'string') &&
        (!profiles[index].authGroupName || typeof profiles[index].authGroupName === 'string')
      )
    ) {
      return false;
    }
  }

  return true;
}

detta är på inget sätt lika konkret och kraftfullt som funktionen skriven av @jcalz.