是否可以输入不止一种类型的提示?

Posted

技术标签:

【中文标题】是否可以输入不止一种类型的提示?【英文标题】:Is it possible to type hint more than one type? 【发布时间】:2018-03-28 06:43:22 【问题描述】:

我可以使用类型提示允许两种不同的类型吗?

例如参数$requester 可以是UserFile

function log (User|File $requester) 


【问题讨论】:

如果两个类有一个共同的继承或接口(例如Loggable),那么你可以使用它作为类型提示 Is it possible to specify more than one type hint for a parameter?的可能重复 【参考方案1】:

php 8.0 开始,这将成为可能,包括联合类型。

proposal has been voted 61 in favour to 5 against,实施已准备就绪。

有一个previous RFC 提出这个建议,在另一个答案中提到,但最终被拒绝了。

它将与您问题中的示例完全相同:

class F

   public function foo (File|Resource $f) : int|float  /** implement this**// 

这意味着F::foo() 需要File 或资源,并将返回intfloat

另外几点:

可空性

此外,您可以使用null 声明联合。 A|null 等价于 ?A,但更复杂的声明为 A|B|null 也是可能的。

“假”伪类

也可以使用false 类型作为联合类型声明的一部分。例如。 int|false。这主要是由于历史原因,因为某些内部函数在某些类型的错误条件下返回false。以strpos() 为例。

在这些情况下,更现代的函数可能应该返回 null 或引发异常,但包含此替代方案是为了解决遗留代码。

在继承时添加和删除联合类型的一部分

添加联合类型用于参数类型提示(从而减少函数限制)和删除联合类型用于返回类型提示(使返回类型更具体)。

鉴于上面的 F 类,这是合法的:

class G extends F

    public function foo(File|Resource|string $f) : int  /** **/ 

但这不是:

class H extends F

    public function foo(File $f) : int|float|bool  /** **/ 

【讨论】:

你能输入 PHP 8 的异常吗? 你为什么不能呢?例外是类。 我的意思是输入一个方法可能会抛出异常——而不是返回它。 myMethod(): void|MyException 不起作用 这是一个 return 类型提示。你所说的语法是“此方法返回voidMyException 的实例。没有“可能引发异常”的语法,您需要使用注释或属性。【参考方案2】:

在学术上,这称为type union。

PHP 中的联合类型

如其他答案中所述,您可以通过创建接口、父类型等来作弊,但是除了增加项目的复杂性和 LoC 之外,还有什么意义呢?另外,这不适用于标量类型,因为您无法扩展/实现标量类型。

您会得到相反的结果,而不是使代码更具可读性。除非那些类/接口已经存在并且它们是因为 OOP 而存在,而不是为了解决类型提示问题。

解决方法

PHP 中的规范答案是……好吧,只是不要输入类型提示。该语言不被认为具有复杂而强大的类型系统,试图解决该语言的缺陷并不是一个好的答案。

相反,正确记录您的函数:

/**
 * Description of what the function does.
 *
 * @param User|File $multiTypeArgument Description of the argument.
 *
 * @return string[] Description of the function's return value.
 */
function myFunction($multiTypeArgument)

这至少会为自动完成和静态代码分析带来 IDE 支持。在从事私人项目、网站等工作时就足够了。

在设计公共 API(PHP 库等)时,有时您可能希望对 API 使用者的输入更具防御性。

那么@tilz0R 的答案就是要走的路:

function log($message) 
    if (!is_string($message) && !$message instanceof Message) 
        throw new \InvalidArgumentException('$message must be a string or a Message object.');
    

    // code ...

PHP(几乎)有联合类型的那一天

2015 年 2 月 14 日,Union Types PHP RFC 被提议用于 PHP 7.1。经过讨论和投票,18个“否”对11个“是”被否决。

如果 RFC 已被接受,PHP 将拥有与您显示的完全相同的联合类型 (User|File)。

RFC 有一些缺陷,但它被拒绝的主要原因是 mainteners 投票者非常抗拒改变,尤其是在涉及类型严格性和其他编程范式时(例如 “当默认值采用所有类型的值时,为什么我们需要类型联合”“这对性能不利”)。

【讨论】:

我有点不相信,但我只是检查了一下,很遗憾地去看了 RFC 来思考它,然后决定去看看 RFC 索引,和 UNION TYPES V2 位于 RFC 列表的顶部。哇。它从字面上开始讨论 6 天前。 github.com/php/php-rfcs/pull/1 - 这也是第一个在 GH 上讨论的 RFC,甚至。迷人的时间大声笑 @i336_ 是的,我也认为它会与这个战斗 - wiki.php.net/rfc/mixed-typehint 我反复遇到的联合类型的主要需求之一是 ArrayAccess 不允许传递数组。我从来没有找到一个很好的解决方案来解决这个问题,默认情况下在这些情况下根本没有任何类型提示。 RFC for Union Types 在 PHP 8 中被接受和实现。【参考方案3】:

目前在 PHP 中是不可能的。但是,您可以拥有一个interface,并为UserFile 实现它,然后将该接口用作log() 中的类型提示:

<?php
interface UserFile 


class User implements UserFile 

class File implements UserFile 


// snip

public function log (UserFile $requester) 

【讨论】:

【参考方案4】:

您可以检查函数内部的类型。

function log ($requester) 
    if ($requester instanceof User || $requester instanceof File) 
        //Do your job
    

【讨论】:

【参考方案5】:

或者您可以为每个方法设置 2 种方法和一种动态方法:

function logUser($requester)

  //

function logFile($requester)

  //

还有动态的

function log($requester)

  if ($requester instanceof File) 
    return $this->logFile($requester);
  

  if ($requester instanceof User) 
    return $this->logUser($requester);
  

  throw new LogMethodException();

【讨论】:

【参考方案6】:

您可以为这两个创建父类:

abstract class Parent_class 
class User extends Parent_class 
    // ...

class File extends Parent_class 
    // ...

并在函数中使用

function log (Parent_class $requester) 
    // ... code

【讨论】:

以上是关于是否可以输入不止一种类型的提示?的主要内容,如果未能解决你的问题,请参考以下文章

有没有一种方法可以组合多种类型来输入[重复]

是否可以输入提示 lambda 函数?

是否可以在特征中输入提示 $this?

是否可以在android中制作自动输入提示文本?

mypy,输入提示:Union[float, int] -> 是不是有 Number 类型?

类型提示模型数组