typescript - Hvordan legge til typesikkerhet når man sender funksjoner som argumenter?


Jeg er nysgjerrig på hvordan man korrekt skriver funksjonsparametere, når det gjelder generiske typer.

Når jeg ser på følgende enkle eksempel, har jeg 2 funksjoner: - sayHello & runAnyFunction

Nå er det første kall til sayHello tydeligvis en feil, dataene som sendes til funksjonen har ikke en name-parameter. Perfekt og forventet.

Problemet ligger i neste del, der vi har en funksjon runAnyFunction som tar en funksjon som sin første parameter, obj som sin andre parameter, og ganske enkelt kjører funksjonen ved å sende obj som argument.

De følgende 2 funksjonene i eksemplet nedenfor vil passere Typescript-sjekker uten problemer, og det er mitt problem.

runAnyFunction<Person>(sayHello, {
  name: "Bob"
});

runAnyFunction<Monster>(sayHello, {
  size: "HUGE"
});

Jeg vet at den andre kallingen vil gi meg Hello undefined, noe som er uønsket. Ideelt sett vil jeg at dette kallet skal feile typekontrollen, da dette ikke skal være tillatt.

Min tanke er at gjerningspersonen her er fn-funksjonsparameteren, siden dens egen obj-parameter vil bli tolket som any, men jeg har vanskeligheter med å skrive riktig typisering for dette.

Jeg forstår at i denne demoen er min runAnyFunction en funksjon som egentlig bare kjører hvilken som helst funksjon, men hvis det finnes en foretrukket måte å skrive en funksjon av denne typen på, vil jeg gjerne vite det.

Hvordan kan jeg skrive dette riktig?

Kodeeksempel

// demo.ts

type Fn = (obj) => string;

interface Person {
  name: string;
}

interface Monster {
  size: string;
}

const somePerson: Person = {
  name: "Bob"
};

const someMonster: Monster = {
  size: "HUGE"
};


sayHello(someMonster); // ERROR, 'name' is missing in type. Sure, expected.


// sayHello requires an obj of type Person
export function sayHello(obj: Person): string {
  return `Hello ${obj.name}`;
}

// Simple function that runs any function, passing obj to it
export function runAnyFunction<T>(fn: Fn, obj: T): void {
  const res = fn(obj);
  console.log(res);
}

// Person has name, this works
runAnyFunction<Person>(sayHello, {
  name: "Bob"
});

// I'm passing sayHello, which should require an object with a `name`
// but it does not error
runAnyFunction<Monster>(sayHello, {
  size: "HUGE"
});

Svar #1

Du må gjøre din Fn-type generisk også:

type Fn<T> = (obj: T) => string;
export function runAnyFunction<T>(fn: Fn<T>, obj: T): void {
    const res = fn(obj);
    console.log(res);
}

Grunnen til at du ikke fikk en feil før, er at Fn-typen din implisitt hadde any-typen for sin første parameter:

type Fn = (obj) => string; // obj is implicitly of type any

Relaterte artikler