首页 > 解决方案 > 如何在 Typescript 中测试泛型类型?不是泛型类型的实现,而是作为类型保护?

问题描述

我正在努力将一堆打字稿类型扩展为实用程序库,作为个人“我真的了解打字稿吗?” 项目。

我只是在弄清楚如何对这该死的东西进行单元测试时遇到了很多麻烦。

问题是我想测试当我将错误的类型传递给调用类型的函数时,TSC 会捕获它。它确实如此。辉煌。但是有一个很小的缺陷,我使用的单元测试库(Jest)会抛出一个错误(期望的行为),然后拒绝运行测试套件的其余部分,因为它抛出了一个错误。我什至不能使用“将抛出错误”,因为它不是 Javascript 错误而是 Typescript 错误。

这是我到目前为止的代码。我正在使用 Jest,但我的解决方案不需要特定于 Jest,我有一种有趣的感觉,我可能需要某种专门的测试库。

// src/Either/Either.ts
export type Either<Base, Either, Or> = (Base & Either) | (Base & Or);
// src/either/Either.spec.ts

import { Either } from "./Either";

interface Pet {
  name: string;
}
interface Dog {
  says: "awoo" | "ruff" | "yip" | "bork" | "bark" | "grr";
  willFetch: true;
}
interface Cat {
  says: "meow" | "hiss";
  willFetch: false;
}

describe("Either<Base, Either, Or>", () => {
  it("allows one extension OR the other, but not both, and not neither", () => {
    type CommonPet = Either<Pet, Dog, Cat>;
    const whatAmI = (pet: CommonPet): "dog" | "cat" => {
      if (pet.willFetch === true) {
        return "dog";
      }
      return "cat";
    };
    expect(whatAmI({ name: "Rex", willFetch: true, says: "awoo" })).toBe("dog");
    expect(whatAmI({ name: "Psycho", says: "hiss", willFetch: false })).toBe(
      "cat"
    );
    // these lines will throw a typescript error (desired)
    /* 
     const freak = whatAmI({ name: "Freak", says: "meow", willFetch: true });
     const uncommonPet = whatAmI({ name: "Nagini", says: 'hiss', willFetch: false, isSnake: true });
    */

    // but how to I tell Jest that I WANT it to throw a typescript error?
    interface AristoCat {
      knowsWhereItsAt: boolean;
    }
    interface JellicleCat {
      nightmareFuel: number;
    }
    type MusicalCat = Either<Pet & Cat, AristoCat, JellicleCat>;
    const isGood = (cat: MusicalCat): boolean | never => {
      if ("knowsWhereItsAt" in cat) {
        return true;
      }
      if (cat.nightmareFuel > 0) {
        throw new Error("NOOOOOO!");
      }
      return false;
    };
    expect(
      isGood({
        name: "Duchess",
        says: "meow",
        willFetch: false,
        knowsWhereItsAt: true
      })
    ).toBe(true);
    expect(
      isGood({
        name: "MacCavity (stage)",
        willFetch: false,
        says: "meow",
        nightmareFuel: 0
      })
    ).toBe(false);
    const filmIt = () =>
      isGood({
        name: "MacCavity",
        willFetch: false,
        says: "meow",
        nightmareFuel: 9001
      });
    expect(filmIt).toThrowErrorMatchingInlineSnapshot(`"NOOOOOO!"`);

    // again, we expect this to throw in TYPESCRIPT because it has BOTH.
    /* 
    const dreamIHadOnAcid = isGood({
      name: 'Cheshire', 
      says: 'meow', 
      willFetch: false, 
      knowsWhereItsAt: true, 
      nightmareFuel: 5/7
    })
    */
    // again, we expect this to throw in TYPESCRIPT because it has NEITHER.
    /* 
    const dreamIHadOnAcid = isGood({
      name: 'Tardar Sauce', 
      says: 'meow', 
      willFetch: false, 
    })
    */
  });
});

- 编辑:

还有一个额外的困难。因为我不确定我的 Either 类型是否以它应有的方式工作(或者如果是,编译器无法拾取它。)

我正在为此使用打字稿操场......

export type Either<Base, Either, Or> = (Base & Either) | (Base & Or);

type Pet = {
  name: string;
  says?: string;
}
type DogExtension = {
  playHours: number;
}
type CatExtension = {
  sleepHours: number;
}

type CommonPet = Either<Pet, DogExtension, CatExtension>;

const dog: CommonPet = { name: "rex", says: "awoo", playHours: 5 };
const cat: CommonPet = { name: "manx", says: "meow", sleepHours: 14 };

const testPet = (testName: string, pet: CommonPet): CommonPet => { 
  return pet
}; 

testPet("dog should pass", dog);
testPet("cat should pass", cat);
testPet("catdog should not pass", { ...dog, ...cat }); /* should throw a type error, but doesn't */

这可能是打字稿编译器中的一个错误......而且很容易制作。Dog 是 CommonPet,Cat 是 CommonPet,那么为什么 {...dog, ...cat} 不是普通宠物?除非 - 更有可能的解决方案,我的要么 =(T&E) | (T&O) 只是不像我想象的那样工作。

标签: typescriptunit-testingtypescript-generics

解决方案


我之前遇到过这个问题,并设法让它与dtslint一起工作。我发现这些测试对于编写包含大量通用类型逻辑的库非常有价值,因为我经常遇到修复一件事会导致在其他地方错误地推断类型的场景。

这是我设法让它工作的仓库。具体来说,此文件包含其中一项测试。可以使用yarn type-check或运行类型测试npm run type-check

当某些事情失败时,您将收到如下错误:

Error: /good-form/types/Field.types.ts:19:3
ERROR: 19:3  expect  TypeScript@local expected type to be:
  number
got:
  string

推荐阅读