Repository: python-telegram-bot/python-telegram-bot

Issue:
Type hinting
Fixes Issue #2125 
A better type hinting to allow Tuples along with Lists wherever it can be used that way.


Discussion:

Resolving patch:
diff --git a/telegram/ext/commandhandler.py b/telegram/ext/commandhandler.py
--- a/telegram/ext/commandhandler.py
+++ b/telegram/ext/commandhandler.py
@@ -24,7 +24,7 @@
 from telegram import MessageEntity, Update
 from telegram.ext import BaseFilter, Filters
 from telegram.utils.deprecate import TelegramDeprecationWarning
-from telegram.utils.types import HandlerArg
+from telegram.utils.types import HandlerArg, SLT
 
 from .handler import Handler
 
@@ -49,9 +49,9 @@ class CommandHandler(Handler):
         :class:`telegram.ext.CommandHandler` does *not* handle (edited) channel posts.
 
     Attributes:
-        command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
-            should listen for. Limitations are the same as described here
-            https://core.telegram.org/bots#commands
+        command (:class:`telegram.utils.types.SLT[str]`):
+            The command or list of commands this handler should listen for.
+            Limitations are the same as described here https://core.telegram.org/bots#commands
         callback (:obj:`callable`): The callback function for this handler.
         filters (:class:`telegram.ext.BaseFilter`): Optional. Only allow updates with these
             Filters.
@@ -83,9 +83,9 @@ class CommandHandler(Handler):
         attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info.
 
     Args:
-        command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
-            should listen for. Limitations are the same as described here
-            https://core.telegram.org/bots#commands
+        command (:class:`telegram.utils.types.SLT[str]`):
+            The command or list of commands this handler should listen for.
+            Limitations are the same as described here https://core.telegram.org/bots#commands
         callback (:obj:`callable`): The callback function for this handler. Will be called when
             :attr:`check_update` has determined that an update should be processed by this handler.
             Callback signature for context based API:
@@ -132,7 +132,7 @@ class CommandHandler(Handler):
 
     def __init__(
         self,
-        command: Union[str, List[str]],
+        command: SLT[str],
         callback: Callable[[HandlerArg, 'CallbackContext'], RT],
         filters: BaseFilter = None,
         allow_edited: bool = None,
@@ -297,9 +297,10 @@ class PrefixHandler(CommandHandler):
         attributes to :class:`telegram.ext.CallbackContext`. See its docs for more info.
 
     Args:
-        prefix (:obj:`str` | List[:obj:`str`]): The prefix(es) that will precede :attr:`command`.
-        command (:obj:`str` | List[:obj:`str`]): The command or list of commands this handler
-            should listen for.
+        prefix (:class:`telegram.utils.types.SLT[str]`):
+            The prefix(es) that will precede :attr:`command`.
+        command (:class:`telegram.utils.types.SLT[str]`):
+            The command or list of commands this handler should listen for.
         callback (:obj:`callable`): The callback function for this handler. Will be called when
             :attr:`check_update` has determined that an update should be processed by this handler.
             Callback signature for context based API:
@@ -340,8 +341,8 @@ class PrefixHandler(CommandHandler):
 
     def __init__(
         self,
-        prefix: Union[str, List[str]],
-        command: Union[str, List[str]],
+        prefix: SLT[str],
+        command: SLT[str],
         callback: Callable[[HandlerArg, 'CallbackContext'], RT],
         filters: BaseFilter = None,
         pass_args: bool = False,
diff --git a/telegram/ext/filters.py b/telegram/ext/filters.py
--- a/telegram/ext/filters.py
+++ b/telegram/ext/filters.py
@@ -24,7 +24,7 @@
 
 from abc import ABC, abstractmethod
 from threading import Lock
-from typing import Dict, FrozenSet, List, Match, Optional, Pattern, Set, Union, cast
+from typing import Dict, FrozenSet, List, Match, Optional, Pattern, Set, Tuple, Union, cast
 
 from telegram import Chat, Message, MessageEntity, Update
 
@@ -38,6 +38,7 @@
 ]
 
 from telegram.utils.deprecate import TelegramDeprecationWarning
+from telegram.utils.types import SLT
 
 
 class BaseFilter(ABC):
@@ -280,7 +281,12 @@ def __init__(self, emoji: str = None, name: str = None):
         self.emoji = emoji
 
     class _DiceValues(MessageFilter):
-        def __init__(self, values: Union[int, List[int]], name: str, emoji: str = None):
+        def __init__(
+            self,
+            values: SLT[int],
+            name: str,
+            emoji: str = None,
+        ):
             self.values = [values] if isinstance(values, int) else values
             self.emoji = emoji
             self.name = '{}({})'.format(name, values)
@@ -293,7 +299,7 @@ def filter(self, message: Message) -> bool:
             return False
 
     def __call__(  # type: ignore[override]
-        self, update: Union[Update, List[int]]
+        self, update: Union[Update, List[int], Tuple[int]]
     ) -> Union[bool, '_DiceValues']:
         if isinstance(update, Update):
             return self.filter(update.effective_message)
@@ -330,7 +336,7 @@ class _Text(MessageFilter):
         name = 'Filters.text'
 
         class _TextStrings(MessageFilter):
-            def __init__(self, strings: List[str]):
+            def __init__(self, strings: Union[List[str], Tuple[str]]):
                 self.strings = strings
                 self.name = 'Filters.text({})'.format(strings)
 
@@ -340,7 +346,7 @@ def filter(self, message: Message) -> bool:
                 return False
 
         def __call__(  # type: ignore[override]
-            self, update: Union[Update, List[str]]
+            self, update: Union[Update, List[str], Tuple[str]]
         ) -> Union[bool, '_TextStrings']:
             if isinstance(update, Update):
                 return self.filter(update.effective_message)
@@ -381,7 +387,7 @@ class _Caption(MessageFilter):
         name = 'Filters.caption'
 
         class _CaptionStrings(MessageFilter):
-            def __init__(self, strings: List[str]):
+            def __init__(self, strings: Union[List[str], Tuple[str]]):
                 self.strings = strings
                 self.name = 'Filters.caption({})'.format(strings)
 
@@ -391,7 +397,7 @@ def filter(self, message: Message) -> bool:
                 return False
 
         def __call__(  # type: ignore[override]
-            self, update: Union[Update, List[str]]
+            self, update: Union[Update, List[str], Tuple[str]]
         ) -> Union[bool, '_CaptionStrings']:
             if isinstance(update, Update):
                 return self.filter(update.effective_message)
@@ -1070,10 +1076,10 @@ class user(MessageFilter):
                 is specified in :attr:`user_ids` and :attr:`usernames`.
 
         Args:
-            user_id(:obj:`int` | List[:obj:`int`], optional): Which user ID(s) to allow
-                through.
-            username(:obj:`str` | List[:obj:`str`], optional): Which username(s) to allow
-                through. Leading '@'s in usernames will be discarded.
+            user_id(:class:`telegram.utils.types.SLT[int]`, optional):
+                Which user ID(s) to allow through.
+            username(:class:`telegram.utils.types.SLT[str]`, optional):
+                Which username(s) to allow through. Leading '@'s in usernames will be discarded.
             allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no user
                 is specified in :attr:`user_ids` and :attr:`usernames`. Defaults to :obj:`False`
 
@@ -1084,8 +1090,8 @@ class user(MessageFilter):
 
         def __init__(
             self,
-            user_id: Union[int, List[int]] = None,
-            username: Union[str, List[str]] = None,
+            user_id: SLT[int] = None,
+            username: SLT[str] = None,
             allow_empty: bool = False,
         ):
             self.allow_empty = allow_empty
@@ -1098,7 +1104,7 @@ def __init__(
             self._set_usernames(username)
 
         @staticmethod
-        def _parse_user_id(user_id: Union[int, List[int]]) -> Set[int]:
+        def _parse_user_id(user_id: SLT[int]) -> Set[int]:
             if user_id is None:
                 return set()
             if isinstance(user_id, int):
@@ -1106,14 +1112,14 @@ def _parse_user_id(user_id: Union[int, List[int]]) -> Set[int]:
             return set(user_id)
 
         @staticmethod
-        def _parse_username(username: Union[str, List[str]]) -> Set[str]:
+        def _parse_username(username: SLT[str]) -> Set[str]:
             if username is None:
                 return set()
             if isinstance(username, str):
                 return {username[1:] if username.startswith('@') else username}
             return {user[1:] if user.startswith('@') else user for user in username}
 
-        def _set_user_ids(self, user_id: Union[int, List[int]]) -> None:
+        def _set_user_ids(self, user_id: SLT[int]) -> None:
             with self.__lock:
                 if user_id and self._usernames:
                     raise RuntimeError(
@@ -1121,7 +1127,7 @@ def _set_user_ids(self, user_id: Union[int, List[int]]) -> None:
                     )
                 self._user_ids = self._parse_user_id(user_id)
 
-        def _set_usernames(self, username: Union[str, List[str]]) -> None:
+        def _set_usernames(self, username: SLT[str]) -> None:
             with self.__lock:
                 if username and self._user_ids:
                     raise RuntimeError(
@@ -1135,7 +1141,7 @@ def user_ids(self) -> FrozenSet[int]:
                 return frozenset(self._user_ids)
 
         @user_ids.setter
-        def user_ids(self, user_id: Union[int, List[int]]) -> None:
+        def user_ids(self, user_id: SLT[int]) -> None:
             self._set_user_ids(user_id)
 
         @property
@@ -1144,16 +1150,17 @@ def usernames(self) -> FrozenSet[str]:
                 return frozenset(self._usernames)
 
         @usernames.setter
-        def usernames(self, username: Union[str, List[str]]) -> None:
+        def usernames(self, username: SLT[str]) -> None:
             self._set_usernames(username)
 
-        def add_usernames(self, username: Union[str, List[str]]) -> None:
+        def add_usernames(self, username: SLT[str]) -> None:
             """
             Add one or more users to the allowed usernames.
 
             Args:
-                username(:obj:`str` | List[:obj:`str`], optional): Which username(s) to allow
-                    through. Leading '@'s in usernames will be discarded.
+                username(:class:`telegram.utils.types.SLT[str]`, optional):
+                    Which username(s) to allow through.
+                    Leading '@'s in usernames will be discarded.
             """
             with self.__lock:
                 if self._user_ids:
@@ -1164,13 +1171,13 @@ def add_usernames(self, username: Union[str, List[str]]) -> None:
                 parsed_username = self._parse_username(username)
                 self._usernames |= parsed_username
 
-        def add_user_ids(self, user_id: Union[int, List[int]]) -> None:
+        def add_user_ids(self, user_id: SLT[int]) -> None:
             """
             Add one or more users to the allowed user ids.
 
             Args:
-                user_id(:obj:`int` | List[:obj:`int`], optional): Which user ID(s) to allow
-                    through.
+                user_id(:class:`telegram.utils.types.SLT[int]`, optional):
+                    Which user ID(s) to allow through.
             """
             with self.__lock:
                 if self._usernames:
@@ -1182,13 +1189,14 @@ def add_user_ids(self, user_id: Union[int, List[int]]) -> None:
 
                 self._user_ids |= parsed_user_id
 
-        def remove_usernames(self, username: Union[str, List[str]]) -> None:
+        def remove_usernames(self, username: SLT[str]) -> None:
             """
             Remove one or more users from allowed usernames.
 
             Args:
-                username(:obj:`str` | List[:obj:`str`], optional): Which username(s) to disallow
-                    through. Leading '@'s in usernames will be discarded.
+                username(:class:`telegram.utils.types.SLT[str]`, optional):
+                    Which username(s) to disallow through.
+                    Leading '@'s in usernames will be discarded.
             """
             with self.__lock:
                 if self._user_ids:
@@ -1199,13 +1207,13 @@ def remove_usernames(self, username: Union[str, List[str]]) -> None:
                 parsed_username = self._parse_username(username)
                 self._usernames -= parsed_username
 
-        def remove_user_ids(self, user_id: Union[int, List[int]]) -> None:
+        def remove_user_ids(self, user_id: SLT[int]) -> None:
             """
             Remove one or more users from allowed user ids.
 
             Args:
-                user_id(:obj:`int` | List[:obj:`int`], optional): Which user ID(s) to disallow
-                    through.
+                user_id(:class:`telegram.utils.types.SLT[int]`, optional):
+                    Which user ID(s) to disallow through.
             """
             with self.__lock:
                 if self._usernames:
@@ -1250,10 +1258,10 @@ class via_bot(MessageFilter):
                 is specified in :attr:`bot_ids` and :attr:`usernames`.
 
         Args:
-            bot_id(:obj:`int` | List[:obj:`int`], optional): Which bot ID(s) to allow
-                through.
-            username(:obj:`str` | List[:obj:`str`], optional): Which username(s) to allow
-                through. Leading '@'s in usernames will be discarded.
+            bot_id(:class:`telegram.utils.types.SLT[int]`, optional):
+                Which bot ID(s) to allow through.
+            username(:class:`telegram.utils.types.SLT[str]`, optional):
+                Which username(s) to allow through. Leading '@'s in usernames will be discarded.
             allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no user
                 is specified in :attr:`bot_ids` and :attr:`usernames`. Defaults to :obj:`False`
 
@@ -1263,8 +1271,8 @@ class via_bot(MessageFilter):
 
         def __init__(
             self,
-            bot_id: Union[int, List[int]] = None,
-            username: Union[str, List[str]] = None,
+            bot_id: SLT[int] = None,
+            username: SLT[str] = None,
             allow_empty: bool = False,
         ):
             self.allow_empty = allow_empty
@@ -1277,7 +1285,7 @@ def __init__(
             self._set_usernames(username)
 
         @staticmethod
-        def _parse_bot_id(bot_id: Union[int, List[int]]) -> Set[int]:
+        def _parse_bot_id(bot_id: SLT[int]) -> Set[int]:
             if bot_id is None:
                 return set()
             if isinstance(bot_id, int):
@@ -1285,14 +1293,14 @@ def _parse_bot_id(bot_id: Union[int, List[int]]) -> Set[int]:
             return set(bot_id)
 
         @staticmethod
-        def _parse_username(username: Union[str, List[str]]) -> Set[str]:
+        def _parse_username(username: SLT[str]) -> Set[str]:
             if username is None:
                 return set()
             if isinstance(username, str):
                 return {username[1:] if username.startswith('@') else username}
             return {bot[1:] if bot.startswith('@') else bot for bot in username}
 
-        def _set_bot_ids(self, bot_id: Union[int, List[int]]) -> None:
+        def _set_bot_ids(self, bot_id: SLT[int]) -> None:
             with self.__lock:
                 if bot_id and self._usernames:
                     raise RuntimeError(
@@ -1300,7 +1308,7 @@ def _set_bot_ids(self, bot_id: Union[int, List[int]]) -> None:
                     )
                 self._bot_ids = self._parse_bot_id(bot_id)
 
-        def _set_usernames(self, username: Union[str, List[str]]) -> None:
+        def _set_usernames(self, username: SLT[str]) -> None:
             with self.__lock:
                 if username and self._bot_ids:
                     raise RuntimeError(
@@ -1314,7 +1322,7 @@ def bot_ids(self) -> FrozenSet[int]:
                 return frozenset(self._bot_ids)
 
         @bot_ids.setter
-        def bot_ids(self, bot_id: Union[int, List[int]]) -> None:
+        def bot_ids(self, bot_id: SLT[int]) -> None:
             self._set_bot_ids(bot_id)
 
         @property
@@ -1323,16 +1331,17 @@ def usernames(self) -> FrozenSet[str]:
                 return frozenset(self._usernames)
 
         @usernames.setter
-        def usernames(self, username: Union[str, List[str]]) -> None:
+        def usernames(self, username: SLT[str]) -> None:
             self._set_usernames(username)
 
-        def add_usernames(self, username: Union[str, List[str]]) -> None:
+        def add_usernames(self, username: SLT[str]) -> None:
             """
             Add one or more users to the allowed usernames.
 
             Args:
-                username(:obj:`str` | List[:obj:`str`], optional): Which username(s) to allow
-                    through. Leading '@'s in usernames will be discarded.
+                username(:class:`telegram.utils.types.SLT[str]`, optional):
+                    Which username(s) to allow through.
+                    Leading '@'s in usernames will be discarded.
             """
             with self.__lock:
                 if self._bot_ids:
@@ -1343,14 +1352,14 @@ def add_usernames(self, username: Union[str, List[str]]) -> None:
                 parsed_username = self._parse_username(username)
                 self._usernames |= parsed_username
 
-        def add_bot_ids(self, bot_id: Union[int, List[int]]) -> None:
+        def add_bot_ids(self, bot_id: SLT[int]) -> None:
             """
 
             Add one or more users to the allowed user ids.
 
             Args:
-                bot_id(:obj:`int` | List[:obj:`int`], optional): Which bot ID(s) to allow
-                    through.
+                bot_id(:class:`telegram.utils.types.SLT[int]`, optional):
+                    Which bot ID(s) to allow through.
             """
             with self.__lock:
                 if self._usernames:
@@ -1362,13 +1371,14 @@ def add_bot_ids(self, bot_id: Union[int, List[int]]) -> None:
 
                 self._bot_ids |= parsed_bot_id
 
-        def remove_usernames(self, username: Union[str, List[str]]) -> None:
+        def remove_usernames(self, username: SLT[str]) -> None:
             """
             Remove one or more users from allowed usernames.
 
             Args:
-                username(:obj:`str` | List[:obj:`str`], optional): Which username(s) to disallow
-                    through. Leading '@'s in usernames will be discarded.
+                username(:class:`telegram.utils.types.SLT[str]`, optional):
+                    Which username(s) to disallow through.
+                    Leading '@'s in usernames will be discarded.
             """
             with self.__lock:
                 if self._bot_ids:
@@ -1379,13 +1389,13 @@ def remove_usernames(self, username: Union[str, List[str]]) -> None:
                 parsed_username = self._parse_username(username)
                 self._usernames -= parsed_username
 
-        def remove_bot_ids(self, bot_id: Union[int, List[int]]) -> None:
+        def remove_bot_ids(self, bot_id: SLT[int]) -> None:
             """
             Remove one or more users from allowed user ids.
 
             Args:
-                bot_id(:obj:`int` | List[:obj:`int`], optional): Which bot ID(s) to disallow
-                    through.
+                bot_id(:class:`telegram.utils.types.SLT[int]`, optional):
+                    Which bot ID(s) to disallow through.
             """
             with self.__lock:
                 if self._usernames:
@@ -1429,10 +1439,11 @@ class chat(MessageFilter):
                 is specified in :attr:`chat_ids` and :attr:`usernames`.
 
         Args:
-            chat_id(:obj:`int` | List[:obj:`int`], optional): Which chat ID(s) to allow
-                through.
-            username(:obj:`str` | List[:obj:`str`], optional): Which username(s) to allow
-                through. Leading `'@'` s in usernames will be discarded.
+            chat_id(:class:`telegram.utils.types.SLT[int]`, optional):
+                Which chat ID(s) to allow through.
+            username(:class:`telegram.utils.types.SLT[str]`, optional):
+                Which username(s) to allow through.
+                Leading `'@'` s in usernames will be discarded.
             allow_empty(:obj:`bool`, optional): Whether updates should be processed, if no chat
                 is specified in :attr:`chat_ids` and :attr:`usernames`. Defaults to :obj:`False`
 
@@ -1443,8 +1454,8 @@ class chat(MessageFilter):
 
         def __init__(
             self,
-            chat_id: Union[int, List[int]] = None,
-            username: Union[str, List[str]] = None,
+            chat_id: SLT[int] = None,
+            username: SLT[str] = None,
             allow_empty: bool = False,
         ):
             self.allow_empty = allow_empty
@@ -1457,7 +1468,7 @@ def __init__(
             self._set_usernames(username)
 
         @staticmethod
-        def _parse_chat_id(chat_id: Union[int, List[int]]) -> Set[int]:
+        def _parse_chat_id(chat_id: SLT[int]) -> Set[int]:
             if chat_id is None:
                 return set()
             if isinstance(chat_id, int):
@@ -1465,14 +1476,14 @@ def _parse_chat_id(chat_id: Union[int, List[int]]) -> Set[int]:
             return set(chat_id)
 
         @staticmethod
-        def _parse_username(username: Union[str, List[str]]) -> Set[str]:
+        def _parse_username(username: SLT[str]) -> Set[str]:
             if username is None:
                 return set()
             if isinstance(username, str):
                 return {username[1:] if username.startswith('@') else username}
             return {chat[1:] if chat.startswith('@') else chat for chat in username}
 
-        def _set_chat_ids(self, chat_id: Union[int, List[int]]) -> None:
+        def _set_chat_ids(self, chat_id: SLT[int]) -> None:
             with self.__lock:
                 if chat_id and self._usernames:
                     raise RuntimeError(
@@ -1480,7 +1491,7 @@ def _set_chat_ids(self, chat_id: Union[int, List[int]]) -> None:
                     )
                 self._chat_ids = self._parse_chat_id(chat_id)
 
-        def _set_usernames(self, username: Union[str, List[str]]) -> None:
+        def _set_usernames(self, username: SLT[str]) -> None:
             with self.__lock:
                 if username and self._chat_ids:
                     raise RuntimeError(
@@ -1494,7 +1505,7 @@ def chat_ids(self) -> FrozenSet[int]:
                 return frozenset(self._chat_ids)
 
         @chat_ids.setter
-        def chat_ids(self, chat_id: Union[int, List[int]]) -> None:
+        def chat_ids(self, chat_id: SLT[int]) -> None:
             self._set_chat_ids(chat_id)
 
         @property
@@ -1503,16 +1514,17 @@ def usernames(self) -> FrozenSet[str]:
                 return frozenset(self._usernames)
 
         @usernames.setter
-        def usernames(self, username: Union[str, List[str]]) -> None:
+        def usernames(self, username: SLT[str]) -> None:
             self._set_usernames(username)
 
-        def add_usernames(self, username: Union[str, List[str]]) -> None:
+        def add_usernames(self, username: SLT[str]) -> None:
             """
             Add one or more chats to the allowed usernames.
 
             Args:
-                username(:obj:`str` | List[:obj:`str`], optional): Which username(s) to allow
-                    through. Leading `'@'` s in usernames will be discarded.
+                username(:class:`telegram.utils.types.SLT[str]`, optional):
+                    Which username(s) to allow through.
+                    Leading `'@'` s in usernames will be discarded.
             """
             with self.__lock:
                 if self._chat_ids:
@@ -1523,13 +1535,13 @@ def add_usernames(self, username: Union[str, List[str]]) -> None:
                 parsed_username = self._parse_username(username)
                 self._usernames |= parsed_username
 
-        def add_chat_ids(self, chat_id: Union[int, List[int]]) -> None:
+        def add_chat_ids(self, chat_id: SLT[int]) -> None:
             """
             Add one or more chats to the allowed chat ids.
 
             Args:
-                chat_id(:obj:`int` | List[:obj:`int`], optional): Which chat ID(s) to allow
-                    through.
+                chat_id(:class:`telegram.utils.types.SLT[int]`, optional):
+                    Which chat ID(s) to allow through.
             """
             with self.__lock:
                 if self._usernames:
@@ -1541,13 +1553,14 @@ def add_chat_ids(self, chat_id: Union[int, List[int]]) -> None:
 
                 self._chat_ids |= parsed_chat_id
 
-        def remove_usernames(self, username: Union[str, List[str]]) -> None:
+        def remove_usernames(self, username: SLT[str]) -> None:
             """
             Remove one or more chats from allowed usernames.
 
             Args:
-                username(:obj:`str` | List[:obj:`str`], optional): Which username(s) to disallow
-                    through. Leading '@'s in usernames will be discarded.
+                username(:class:`telegram.utils.types.SLT[str]`, optional):
+                    Which username(s) to disallow through.
+                    Leading '@'s in usernames will be discarded.
             """
             with self.__lock:
                 if self._chat_ids:
@@ -1558,13 +1571,13 @@ def remove_usernames(self, username: Union[str, List[str]]) -> None:
                 parsed_username = self._parse_username(username)
                 self._usernames -= parsed_username
 
-        def remove_chat_ids(self, chat_id: Union[int, List[int]]) -> None:
+        def remove_chat_ids(self, chat_id: SLT[int]) -> None:
             """
             Remove one or more chats from allowed chat ids.
 
             Args:
-                chat_id(:obj:`int` | List[:obj:`int`], optional): Which chat ID(s) to disallow
-                    through.
+                chat_id(:class:`telegram.utils.types.SLT[int]`, optional):
+                    Which chat ID(s) to disallow through.
             """
             with self.__lock:
                 if self._usernames:
@@ -1638,8 +1651,8 @@ class _Dice(_DiceEmoji):
         ``MessageHandler(Filters.dice([5, 6]), callback_method)``.
 
     Args:
-        update (:obj:`int` | List[:obj:`int`], optional): Which values to allow. If not
-            specified, will allow any dice message.
+        update (:class:`telegram.utils.types.SLT[int]`, optional):
+            Which values to allow. If not specified, will allow any dice message.
 
     Note:
         Dice messages don't have text. If you want to filter either text or dice messages, use
@@ -1665,13 +1678,14 @@ class language(MessageFilter):
             ``MessageHandler(Filters.language("en"), callback_method)``
 
         Args:
-            lang (:obj:`str` | List[:obj:`str`]): Which language code(s) to allow through. This
-                will be matched using ``.startswith`` meaning that 'en' will match both 'en_US'
-                and 'en_GB'.
+            lang (:class:`telegram.utils.types.SLT[str]`):
+                Which language code(s) to allow through.
+                This will be matched using ``.startswith`` meaning that
+                'en' will match both 'en_US' and 'en_GB'.
 
         """
 
-        def __init__(self, lang: Union[str, List[str]]):
+        def __init__(self, lang: SLT[str]):
             if isinstance(lang, str):
                 lang = cast(str, lang)
                 self.lang = [lang]
diff --git a/telegram/utils/types.py b/telegram/utils/types.py
--- a/telegram/utils/types.py
+++ b/telegram/utils/types.py
@@ -17,7 +17,7 @@
 # You should have received a copy of the GNU Lesser Public License
 # along with this program.  If not, see [http://www.gnu.org/licenses/].
 """This module contains custom typing aliases."""
-from typing import IO, TYPE_CHECKING, Any, Dict, Optional, Tuple, Union
+from typing import IO, TYPE_CHECKING, Any, Dict, List, Optional, Tuple, TypeVar, Union
 
 if TYPE_CHECKING:
     from telegram import InputFile, Update
@@ -33,3 +33,7 @@
 
 ConversationDict = Dict[Tuple[int, ...], Optional[object]]
 """Dicts as maintained by the :class:`telegram.ext.ConversationHandler`."""
+
+RT = TypeVar("RT")
+SLT = Union[RT, List[RT], Tuple[RT, ...]]
+"""Single instance or list/tuple of instances."""


Test patch: