无法在泛型类型上使用Partial

时间:2019-10-04 09:31:17

标签: typescript typescript-generics

我正在尝试制作一个Map类,其中包含将由其他类设置的映射(例如CharacterManager
由于内容取决于其他类,因此我希望Map采用将始终扩展object的泛型。

例如:

// Map.ts
interface MapCellData {
  collides: boolean;
}

class Map<CellData extends object = {}> {
  map!: Array<Array<CellData & MapCellData>>;
}

// CharacterManager.ts
interface CharacterManagerCellData {
  character: null | Character;
}

// And so on...

通过查看类型Array<Array<CellData & MapCellData>>,您可以看到我希望2D数组的每个单元都具有由所有其他类定义的所有属性。

但是,问题在于,我现在必须在首次填充map时提供这些属性。
这不符合我的需要:我不希望我的Map类知道要添加的其他类。每个类都应负责填充其数据(Map负责第一次初始化map,以便其他人可以认为它可用。)

该解决方案对我来说似乎很简单:由于第一次填充地图时未设置CellData属性,因此我不得不说它们是可选的。当一个类想要使用它时,它需要检查它是否被设置。

根据这种推理,我将Array<Array<CellData & MapCellData>>更改为Array<Array<Partial<CellData & MapCellData>>>。 (只需添加Partial)。

现在是真正的问题:这不起作用,但是没有有关错误的信息,我不知道我做错了什么。
TypeScript仅声明Type '{ collides: true; }' is not assignable to type 'Partial<CellData & MapCellData>'.

这是一个产生错误的完整示例:

interface CharacterManagerCellData {
  character: null | string;
}

interface CollisionCellData {
    collides: boolean;
}

// Directly using partial without generic works fine
type BasicCellData = Partial<CharacterManagerCellData & CollisionCellData>;

const myCell: BasicCellData = {
  collides: true
}

// Using the same pattern with a generic throws a typescript error
function myFunc<CellData extends object = {}>() {
  type RealCellData = Partial<CellData & CollisionCellData>;

  const cell: RealCellData = {
    collides: true
  }
}

我正在使用最新版本的TypeScript(3.6.3),并且也会发生此错误in the playground

1 个答案:

答案 0 :(得分:2)

Typescript对于错误没有错。您永远不能安全地将具体值分配给泛型类型。考虑以下示例:

<?php include'index.php';?>
<!doctype html>
<html lang="en">

<head>
<meta charset="UTF-8">
<title>Untitled Document</title>
<link rel="stylesheet" href="style.css">
</head>

<body>
<div class="box">
    <div class="form_input">
        <form method="post" action="">
            <input type="text" name="username" placeholder="Enter 
Username"><br><br>
            <input type="password" name="password" placeholder="Enter 
Password"><br>
            <br><input type="submit" name="send">
        </form>
        <?php
    if(isset ($_post['send'])){
$object = new login();
$object->insertintotb($_post['username'], $_post['password']);
foreach($object->errors as $error){
echo $error;
}

}
    ?>
    </div>
</div>


</body>

</html>
考虑到传入的类型,

myFunc<{ collides: false }>() 应该是collides而不是false,但是您可以使用true对其进行初始化。这显然是错误的。给定true的通用功能对任何有效。此限制意味着通常无法实例化对象并将其分配给泛型类型的引用(在这种情况下,可以使用空对象,因为T使得所有类型都是可选的,而与类型Partial无关)

此约束存在漏洞,例如允许进行赋值,但是对于泛型类型,如果typescript无法确定属性的最终类型是什么,甚至将无法进行简单赋值:

const cell: RealCellData = {}

如果可以接受某些类型会导致类型不一致的类型,则可以使用类型断言:

// this is better
function myFunc<CellData extends CollisionCellData >() {
  type RealCellData = Partial<CellData>;

  const cell: RealCellData = { //This is still an error
     collides: true
  } 
  cell.collides = true; // this is ok beacuse ts knows collide is boolean
}


//this is not ok 
function myFunc<CellData extends object >() {
  type RealCellData = Partial<CellData & CollisionCellData>;

  const cell: RealCellData = { //This is still an error
     collides: true
  } 
  cell.collides = true; // this is not ok, beacuse TS does not know if CellData has collides and what it's type may be
}

Play