Vad är användningsfallet för att ha en odefinierad nyckel jämfört med en icke definierad nyckel inom ett objekt i Typescript?


Jag analyserar ett filnamn med TypeScript och ett reguljärt uttryck så här:

import { Filename } from '../models/filename';

const FILENAME_REGEX = /^(?<slug>.*?)(\.(?<language>[a-z]{2}))?(\.|\-(?<region>[A-Z]{2})\.)(?<extension>[a-z]*)$/i;

export const parseFilename = (filename: string): Filename => {
  const result = FILENAME_REGEX.exec(filename);
  if (result?.groups && result.groups['slug'] && result.groups['extension']) {
    return {
      slug: result.groups['slug'],
      language: result.groups['language'],
      region: result.groups['region'],
      extension: result.groups['extension']
    };
  }

  throw new Error("Filename doesn't match pattern");
};

Filnamnsmodellen ser ut så här:

export interface Filename {
  slug: string;
  language?: string;
  region?: string;
  extension: string;
}

Min fråga är, om region-gruppen är odefinierad för ett filnamn page.en.md, kommer jag att få ett objekt returnerat som ...

{
  slug: "page",
  language: "en",
  region: undefined,
  extension: "md"
}

.. Finns det ett rent sätt utan mycket if och else att få ett objekt som detta?

{
  slug: "page",
  language: "en",
  extension: "md"
}

Vad är motiveringen eller användningsfallet för att ha en odefinierad nyckel och en icke-definierad nyckel?

Svar #1

Jag är inte säker på vad du anser är "rent". Om du vill använda funktionell programmering och inte har något emot att göra lite typkontroll kan du göra det så här:

return (['slug', 'language', 'region', 'extension'] as const).
  filter(k => typeof result.groups?.[k] !== "undefined").reduce(
    (f, k) => (f[k] = result.groups?.[k]! , f),
    {} as Filename);

som har följande utdata:

console.log(parseFilename("foo.html")) 
// {slug: "foo", extension: "html"}
console.log(parseFilename("bar.en.html")) 
// {slug: "bar", language: "en", extension: "html"}
console.log(parseFilename("baz.en-us.html")) 
// {slug: "baz", language: "en", region: "us", extension: "html"}

Att byta if/else mot filter kanske inte är en stor vinst, men kanske uppfyller det dina behov?

Men personligen skulle jag bara lämna undefined-egenskapsvärdena där om du inte vet med säkerhet att de orsakar problem någonstans. Skillnaden mellan en saknad egenskap och en närvarande men undefined-egenskap är observerbar (t.ex. prop in obj är false i det första fallet och true i det senare fallet) men på många sätt är de desamma (t.ex. obj[prop] utvärderas till undefined). TypeScript gör egentligen ingen konsekvent åtskillnad mellan dem, så det är svårt ens att representera skillnaden i språket. Så jag skulle rekommendera att bara vara lite försiktig med att inte använda in eller liknande när du kontrollerar valfria egenskaper.

Okej, hoppas det hjälper; lycka till!

Playground-länk till kod

Svar #2

Ett sätt som kommer i åtanke är att endast definiera nödvändiga egenskaper i objektliteralet och sedan, villkorligt, lägga till valfria egenskaper.

export interface Filename {
  slug: string;  
  language?: string;
}

const f: Filename = {
  slug: 'slug'
};

const lang = undefined;
if (lang) { 
 f.language = lang;
}

Den uppenbara skillnaden mellan att ha en egenskap med värdet undefined och att inte ha egenskapen alls är att i det första fallet rapporteras egenskapen av Object.keys()