输入类型定义时输出非未定义类型

Output non-undefined type when input type is defined

我刚刚写了这个函数:

const cloneUrl = <T extends (URL | undefined)>(url: T): T => url instanceof URL
  ? new URL(url.toString())
  : undefined;

然而,这有错误(或者至少,错误非常接近于此):

'URL | undefined' is not assignable to type 'T'. 'T' could be instantiated with an arbitrary type which could be unrelated to 'URL | undefined'.

Type 'undefined' is not assignable to type 'T'. 'undefined' is assignable to the constraint of type 'T', but 'T' could be instantiated with a different subtype of constraint 'URL | undefined'.

显然我没有掌握 TypeScript 的一些基本知识...

我的目标是这样当 cloneUrl 被称为 URL 的类型调用时,return 类型是 URL,但是当cloneUrl 被调用的类型是 URL | undefined,return 类型是 URL | undefined.

示例:

class UrlWrapper {
  private readonly _urlOne: URL
  private readonly _urlTwo?: URL

  constructor(urlOne: URL, urlTwo?: URL) {
    this._urlOne = urlOne;
    this._urlTwo = urlTwo;
  }

  get urlOne(): URL { return cloneUrl(this._urlOne); }
  get urlTwo(): URL | undefined { return cloneUrl(this._urlTwo); }
}

看看 urlWrapper.urlOne 应该始终是一个不可定义的值,但 urlTwo 可以吗?

我尝试了不同类型的约束:

我试过不同的参数:

我试过投射结果:

我试过将私有字段设为可选,或更改其类型。

我试过使用类型检查器函数:

...还有更多。

但是关于 TypeScript 如何思考类型,我没有正确的心智模型,所以我就是不明白。

我最近处理了这个问题,发现以下内容似乎可以使用带有 conditional return type 的泛型。 any 的使用很丑陋,但我不确定如何避免这种情况。这是巫术,我没有完全理解,但类型被推断为您所期望的。

export function cloneUrl<T extends URL | undefined>(value: T): T extends URL ? URL : undefined {
    return value instanceof URL ? new URL(value.toString()) : (undefined as any);
}

let source: URL | undefined = new URL("test");
cloneUrl(source); // URL
source = undefined;
cloneUrl(source); // undefined
let optionalSource: URL | undefined;
cloneUrl(optionalSource); // URL or undefined
cloneUrl(new Date()); // Nope: Date isn't a URL