• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed Otypowanie własności obiektu - typescript

Object Storage Arubacloud
+1 głos
222 wizyt
pytanie zadane 16 lutego 2022 w JavaScript przez poldeeek Mądrala (5,980 p.)
zamknięte 16 lutego 2022 przez poldeeek

Hej, chciałbym aby przekazywany do sendMessage() argument message mógł być tylko typu któregoś z parametrów obiektu messages. 
Mam javascriptowy obiekt message:

const messages: IMessages = {
  first: 'The first message',
  second: (txt: string) => 'The second message' + txt
}

interface IMessages {
  first: string; 
  second: (txt: string) => string;}

I chciałbym przekazywać własności tego obiektu w funkcji:

const sendMessage = (message) => {
  [...]
}

i wywoływać ją w taki sposób

sendMessage(messages.first)


Próbowałem np stworzyć typ dla parametrów wiadomości:

export type myMessage = string | ((...args: unknown[]) => string);

i wtedy interfejs dla messages i funkcja sendMessage wyglądałyby tak:

interface IMessages {
  first: MyMessage; 
  second: MyMessage;
}

[...]

const sendMessage = (message: MyMessage) => ...

Ale takie rozwiązanie nie działa kiedy przekazuje w argumencie własność, która jest funkcją, np:

sendMessage(messages.second('jakis teskt'))

Dostaje taki błąd

TS2349: This expression is not callable.
   Not all constituents of type 'myMessage' are callable.
     Type 'string' has no call signatures.

Dla message.frist, który jest stringiem ten błąd się nie pojawia.
 

komentarz zamknięcia: znaleziono rozwiązanie

1 odpowiedź

+1 głos
odpowiedź 16 lutego 2022 przez ScriptyChris Mędrzec (190,190 p.)
wybrane 16 lutego 2022 przez poldeeek
 
Najlepsza

Jeśli chcesz żeby argument message w funkcji sendMessage akceptował tylko wartości odpowiadające kluczom obiektu messages (o interfejsie IMessages), to skorzystaj z keyof i (opcjonalnie, jako kombinacja) typeof:

  • keyof typeof messages - wyciągasz klucze z typu obiektu messages
  • albo po prostu keyof IMessages - wyciągasz klucze z interfejsu IMessages

Przykład: https://www.typescriptlang.org/play?#code/MYewdgzgLgBAtgUwhAhgcyQLhgSQLJKoYQwC8MA3gFAwwBmAlgE7TYDkAKgBYL3PTxC6BGwA0NGBASgwAE2wAKKAA8o2aEwZg0ASjIA+GJx6Tp4WYOTC2MANQwVUKgF8aVLVARM6KYL3xCxJQSjCxqklCa2gDcMBJSMvIwSqrqkVq6BhFRaNEuVFQyAlJyBFYYZMmI5QjYANYIAJ4gdA6NAA4ILZZESHqkhtS0APTDMHUgsgB0M-lUozAoADYARiCF4MUIpYEIAEyVCtW99U3dATUQ-YMSCxPTs85AA

komentarz 16 lutego 2022 przez ScriptyChris Mędrzec (190,190 p.)

Jeśli messages.second to zawsze jest funkcja, to nie możesz otypować tego jako string | () => Message, bo to znaczy, że to może być string lub funkcja. Jeśli masz jakąś funkcję, która zwraca string, to możesz użyć konstrukcji ReturnType by wziąć zwracany typ z funkcji. Aczkolwiek wtedy - jeśli chcesz dopuszczać tylko określone stringi, a nie dowolne - musisz konkretnie otypować zwracany typ tej funkcji.

Może opisz konkretnie co próbujesz zrobić? Bo z Twoich opisów wnioskuję, że chcesz, żeby funkcja sendMessage przyjmowała jako argument tylko string, który jest określonego podtypu (mogą to być z góry określone teksty, ale nie dowolny string), ale w między czasie przewija się też interfejs, który uwzględnia jakąś funkcję, która może zwracać ten "konkretny" string. Może po prostu stwórz typ, który obejmie konkretne stringi i ten typ wstaw do intefejsu oraz jako typ zwracany dla funkcji? Tylko też nie wiem jak tworzysz te różne funkcje, które miały by zwracać konkretny string.

1
komentarz 16 lutego 2022 przez poldeeek Mądrala (5,980 p.)

Chcę zrobić funkcję, która będzie wysyłała maile. Treść tych maili chcę mieć w tym obiekcie messages. I teraz problemy są przez to, że niektóre maile są statycznymi stringami, czyli ta własność first z naszego przykładu, ale są też wiadomości dynamiczne generowane przez funkcje na podstawie przekazanych argumentów - second z przykładu.
Bardziej treściwy przykład:

const messages = {
  passwordBlocked: 'Your login attempt failed because your password is blocked.',
  accountBlocked: (date: Date) => `Your account is blocked until ${date}.`
}

i potem jest funkcja od wysyłania maili:
 

public sendEmail(user, message: string) {
  mailer.sendEmail(user.email, 'title', message)
}

. Funckję sendEmail wykonujemy wtedy tak:
 

sendEmail(user, messages.passwordBlocked)

LUB 

sendEmail(user, messages.accountBlocked(blockDate))


I tutaj chciałbym to otypować tak, aby do funkcji sendEmail można było przekazać tylko i wyłącznie któryś z parametrów obiektu message - w praktyce w obu rzeczach będzie tam szedł tylko string, bo funkcja finalnie też zwróci stringa, jednak chciałbym żeby to był string bezpośrednio wygenerowany/pobrany z obiektu messages.

1
komentarz 16 lutego 2022 przez ScriptyChris Mędrzec (190,190 p.)

TypeScript nie bardzo radzi sobie z dynamicznie generowanymi wartościami, bo istnieje tylko w fazie pisania kodu, a po kompilacji już go nie ma (zostaje sam JavaScript). Nie wiem czy jest sens aż tak restrykcyjnie typować treści wiadomości, wliczając w to te generowane z funkcji.

Mógłbyś skorzystać z interpolacji typów stringów, gdzie do typu podajesz część obowiązkową (to co już masz jako unię stringów) oraz końcówkę o określonym ogólnym typie string. Ma to taką wadę, że string przyjmie każdy znak, więc np. taki ogólny (dynamicznie generowany) fragment stringa "asd123 []{}" też będzie akceptowany, ale przynajmniej będziesz mieć pilnowanie fragmentu "obowiązkowego".

Przykład: https://www.typescriptlang.org/play?#code/C4TwDgpgBAKgshAzoghgc2gXigchgC2gDMBLAJ0WCgFslUMcoAfXAZQgGMB7AOwBMaddBEYscAeWCEyg5MJwBuKAHplUFAGsOEMMACuAK2igANhq5QA7iGokIPEr2iUyJHmhIAoUJFgBhPUouagQ5DChsAAMAEgBvWFD6aABfAFpUgD44lzc0ZMilVSgDADpLEqgwE0CoPhAeFFsOAC8GqHNAEUAOAGfLDRRKrjAyZog+Zt4BgCJ0jKnPTzdgCDIiFG0oAElE4UQoWM8oKFIKYAAuBKEMABpDqEROXj4LmACgkKuIT2SF7h5Ke72Pg7cLYAAUtDCEAu20+iAA2hoICAuEQtiCkABdACUEQy+zuRXMfBKpO+Cwe-AxYLwhGO5ABkKSOGxhTUXA0nkpwM+NPYfwETPkrJU7M5XKB1NpxAZVCFGFmy0oLLZUA5EqpvJw-KesiSiqQwBVorV4u5UpQiD4xqKACNAEKAgFBAASpKAOCCtFA8ED3YCudwanlQvnBaDykQiu1Ol1uuye72+-1oQNSggy0564SkkpKo2RtQO51QV3u4YoSwkPj2rjWKBOloODRQKbZqZAA

komentarz 16 lutego 2022 przez poldeeek Mądrala (5,980 p.)

Hmm no rozumiem, to widzę, że w taki sposób mi się nie uda to co chcę..
Mam jeszcze pomysł, żeby zrobić to regexem, i jako message przekazywać obiekt z argumentami.

const interface EmailMessage {
  message: typeof messages[keyof typeof messages]
  params: {
    [name: string]: unknown;
  }
}

const messages = {
  accountBlocked: 'Your account is blocked until {{date}}'
}

const sendEmail = (user: User, title: string, message: EmailMessage) => [...]

sendEmail(user, 'title', {
  message: messages.accountBlocked,
  params: {
    date: blockDate
  }
})


W sendEmail podmieniłbym wszystkie {{text}} na wartości podane w params :P Zawsze to jest jakiś pomysł i mimo, że nie mam kontroli nad argumentami przekazywanymi do wiadomości, to chociaż kontroluję to, że wszystkie wiadomości maili są w jednym miejscu i jak ktoś będzie chciał dodać kolejną to będzie musiał ją dodać do obiektu message ;)

Podobne pytania

+1 głos
1 odpowiedź 285 wizyt
pytanie zadane 16 marca 2021 w JavaScript przez Author[] Gaduła (3,130 p.)
+1 głos
0 odpowiedzi 162 wizyt
0 głosów
1 odpowiedź 279 wizyt

92,566 zapytań

141,420 odpowiedzi

319,609 komentarzy

61,952 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...