Typescript: Hvordan unngå å gjenta objektdeklarasjon som en parameter?


Motivasjon

Jeg skulle gjerne brukt objekt som en funksjonsparameter. Dette lar den som kaller tydelig definere argumentene med de angitte feltnavnene, noe som gjør gjennomganger enklere.

Problem

Dette er imidlertid ikke særlig hyggelig å håndtere hvis du bruker implements og extends. Dette er det jeg gjør nå.

src/domain/ServiceInterface.ts
export interface ServiceInterface {
  doesThings(args: {
    awesomeFieldName: string;
    isThisAwesomeFieldName?: string;
    ohWaitMoreAwesomeFieldName?: string;
  }): boolean;
src/domain/ComposedServiceInterface.ts
import { ServiceInterface } from "./domain/ServiceInterface";
export type ComposedServiceInterface = ServiceInterface & { hello: () => string };
src/implementations/ComposedServiceImplementation.ts
import { ComposedServiceInterface } from "./domain/ComposedServiceInterface";
export class ComposedServiceImplementation implements ComposedServiceInterface {
  doesThings(args: {
    awesomeFieldName: string;
    isThisAwesomeFieldName?: string;
    ohWaitMoreAwesomeFieldName?: string;
  }): boolean {
    return true;
  }
}

Forsøk

1. Bruke type/interface for objektparameteren

src/domain/ServiceInterface.ts
export type DoesThingsParameter = {
    awesomeFieldName: string;
    isThisAwesomeFieldName?: string;
    ohWaitMoreAwesomeFieldName?: string;
  };
export interface ServiceInterface {
  doesThings(args: DoesThingsParameter): boolean;
src/domain/ComposedServiceInterface.ts
import { ServiceInterface } from "./domain/ServiceInterface";
export type ComposedServiceInterface = ServiceInterface & { hello: () => string };
src/implementations/ComposedServiceImplementation
import { ComposedServiceInterface } from "./domain/ComposedServiceInterface";
import { DoesThingsParameter } from "./domain/ServiceInterface";
export class ComposedServiceImplementation implements ComposedServiceInterface {
  doesThings(args: DoesThingsParameter): boolean {
    return true;
  }
}

Innvendig: Jeg liker ikke denne implementeringen fordi src/implementations/ComposedServiceImplementation ikke burde trenge å importere fra src/domain/ServiceInterface siden den kun implementerer ComposedServiceInterface

2. Bruke Parameter verktøytype

ref: https://www.typescriptlang.org/docs/handbook/utility-types.html#parameterstype

Jeg får det ikke til å fungere med TypeScript når jeg bruker en klassemetode, f.eks. Parameter<ComposedServiceInterface.doesThings>

Kan noen gi noen råd?

Svar #1

Den riktige måten å gjenbruke typer/grensesnitt på er Forsøk#1. Når du trenger å gjenbruke noen typedefinisjoner, kan du trekke dem ut. Hvis du er bekymret for å importere fra src/domain/ServiceInterface i src/implementations/ComposedServiceImplementation, kan du alltid opprette en felles fil for typer og legge vanlige typer der og importere dem i begge filene.

Det ene punktet å merke seg her er at du bare importerer typer fra src/domain/ServiceInterface, så denne importen vil bli fjernet etter at du kompilerer TS-koden din. Hvis du vil vite om du importerer en type eller en verdi, kan du bruke type-only import som: import type { DoesThingsParameter } from "./domain/ServiceInterface"; Dette vil gi koden din mer lesbarhet.

Når det gjelder Forsøk #2, prøv å unngå å bruke komplekse nytteverdityper som Parameter eller ConstructorParameter for ofte, da implementeringen deres er ganske kompleks og Typescript må gjøre mye arbeid. Disse typene brukes generelt for å få typer for argumenter som du ikke har direkte tilgang til. For eksempel, hvis du bruker et bibliotek som ikke leverer Typescript-definisjoner og du vil bruke en funksjon fra dette biblioteket.

// some-library.ts
export function configure({
 config1 = 'somevalue',
 retries = 1
}) {
  ...code
}

Nå vil du bruke denne configure-funksjonen, men ønsker å opprette et konfigurasjonsobjekt utenfor.

// helper.ts
import { configure } from 'some-lib';

const configuration = {
 config1: 'overrideValue',
 unknownProperty: 'unknownValue'
}
function helper() {
 const lib = configure(configuration)
...some work here
 }

Nå har konfigurasjonen alle egenskapene som configure-funksjonen trenger, men den har lagt til en ny egenskap som ikke finnes i configure-argumentene. I dette tilfellet må du bruke ParameterType for å få type-definisjonen av configure-argumentene.