TypeScript-类型约束,仅允许类型,文字或实例

时间:2020-05-13 09:14:36

标签: typescript

由于TypeScript的积极类型擦除,编译器会删除类型注释,接口,类型和泛型类型参数,因此很难对某些类型约束进行建模。以下是我正在尝试建模的示例以及到目前为止我已经成功实现的示例。

参数必须是类型本身,而不是类型的实例或文字

可以通过如下介绍Type<T>来解决此问题...

type Type<T = any> = {
    new(...args: any[]): T;
    readonly prototype: T;
    readonly name: string;
}

我们现在可以定义一个函数,将参数限制为类型,而不是类型的实例或文字。

function fn(p: Type<String>) { ... }

fn("") // not okay - it's a String literal
fn(new String()) // not okay - it's a String instance
fn(String) // okay - it's the String type

Type通用的原因是,您可以指定可以传入的任何类型...

function fn(p: Type) { ... }

fn(String) // okay
fn(Number) // okay

参数必须是实例或文字,但不能是类型本身

这不是那么简单,到目前为止,我还无法解决这个问题。给定以下函数定义...

function fn(p: String) { ... }

fn("") // okay - it's a String literal
fn(new String()) // okay - it's a String instance
fn(String) // not okay - it's the String type

那很好,但是当您想允许任何文字或实例但不允许任何类型时,它就会中断...

function fn(p: Object) { ... }

fn("") // okay - it's a String literal
fn(123) // okay - it's a Number literal
fn(new String()) // okay - it's a String instance
fn(String) // okay, BUT should be disallowed because it's the String type.

我不确定第二个问题是否还能解决,但是我想要与Type<T>保持一致的东西,但是相反。例如...

type InstanceOrLiteral<T = any> = {
    // what goes here?
}

function fn(p: InstanceOrLiteral<String>) { ... }

fn("") // okay - it's a String literal
fn(new String()) // okay - it's a String instance
fn(123) // not okay - it's a Number literal
fn(String) // not okay - it's the String type

同样,我希望它保持通用性,以允许任何实例或文字,但不允许任何类型...

function fn(p: InstanceOrLiteral) { ... }

fn("") // okay - it's a String literal
fn(new String()) // okay - it's a String instance
fn(123) // okay - it's a Number literal
fn(String) // not okay - it's the String type
fn(Object) // not okay - it's the Object type
fn(Number) // not okay - it's the Number type

后一种可能吗?

2 个答案:

答案 0 :(得分:2)

使用条件类型可以解决您的第一个问题。

type InstanceOrLiteral<T, WhatInstancesAreDisallowed = unknown> = 
    T extends { new(): WhatInstancesAreDisallowed } ? never : T

从本质上讲,这将检查给定的泛型T是否扩展了构造函数(返回可配置的泛型WhatInstancesAreDisallowed)。如果是这样,则允许的类型为never或换句话说,什么都不是(从来没有任何类型的并集)。当条件类型不匹配(ergo它不是构造函数)时,它默认为T作为允许的类型。

function fn(p: InstanceOrLiteral<String>) {}

fn("") // okay - it's a String literal
fn(new String()) // okay - it's a String instance
fn(123) // not okay - it's a Number literal
fn(String) // not okay - it's the String type

第二期需要一些技巧。您不能简单地在条件类型中将T设置为可选,并将其默认设置为unknown(=所有类型的并集=>将始终与条件类型匹配)或any(=禁止键入=>将导致条件类型为never | T =>的条件类型为T的两个选项的并集,因此也不符合您的要求。相反,您必须推断函数调用的给定类型,并将其传递给InstanceOrLiteral类型。以便条件类型可以与实际给定类型一起使用。

function fn2<T>(p: T & InstanceOrLiteral<T>) { }

fn2("") // okay - it's a String literal
fn2(new String()) // okay - it's a String instance
fn2(123) // okay - it's a Number literal
fn2(String) // not okay - it's the String type
fn2(Object) // not okay - it's the Object type
fn2(Number) // not okay - it's the Number type

Playground

答案 1 :(得分:0)

fn(String)//好的,但是不应该使用BUT,因为它是String 类型。

否,其StringConstructor类型。至少这是操场上智能感知所讲的。

这可以为您解决吗?

function fn<K extends String | Number >(p: K) {

}