打字稿类型推断与设置

如何解决打字稿类型推断与设置

在以下示例中,getValue function无法获得正确的类型。
有什么方法可以通过可配置的设置来推断类型吗?
Example Play

例如

class User {
    name: string;

    constructor(name: string) {
        this.name = name;
    }
}

class Dog {
    name: string;

    constructor(name: string) {
        this.name = name;
    }
}

class Cat {
    name: string;

    constructor(name: string) {
        this.name = name;
    }
}

const settings = [
    { key: 'a',value: 'string' },{ key: 'b',value: 123 },{ key: 'c',value: true },{ key: 'dog',value: Dog },{ key: 'cat',value: Cat },{ key: Dog,{ key: Cat,{ key: User,value: User },]

function getValue(key: any) {
    const item = settings.find(obj => obj.key === key);
    if (item) {
        const { value } = item
        if (typeof value === 'function') {
            return new value('test value');
        } else {
            return value;
        }
    } else {
        throw new Error('not found');
    }
}

当然,我可以使用as Dog强制转换类型。
const dog = getValue('dog') as Dog;
但是我觉得有点多余。
有没有更好的方法解决这个问题?

-编辑2020-10-20 ---
实际上,settings不是预先定义的。
可以在运行时中对其进行修改

最后,我想实现一个像角度依赖注入函数之类的函数
settings's结构如下:

[
{provide: User,useValue: new User('Tom')},{provide: Cat,useClass: Cat},{provide: Dog,useClass: Dog},{provide: AnotherDog,useExiting: Dog},{provide: AnotherCat,useFactory: function(user) { return user.cat; },deps: [User]},]

settings是未预定义的。可以在运行时中对其进行修改

例如

let settings = [
    { key: Cat,]
const cat1 = getValue(Cat); // cat1 is Cat
const dog1 = getValue(Dog); // dog1 is Dog

settings = [
    { key: Cat,]
const cat2 = getValue(Cat); // cat2 is Dog
const dog2 = getValue(Dog); // dog2 is Cat

这就是我的意思,可以在运行时中修改设置。

例如2

let settings = [
    { key: Cat,]
// cat1 is Cat,getValue return type default as Parameter Cat
const cat1 = getValue(Cat);
// dog1 is Dog,getValue return type default as Parameter Dog
const dog1 = getValue(Dog);

settings = [
    { key: Cat,]
// cat2 is Dog,getValue return type is set by generic type Dog
const cat2 = getValue<Dog>(Cat);
// dog2 is Cat,getValue return type is set by generic type Cat
const dog2 = getValue<Cat>(Dog);

可以通过可选的泛型类型像这样实现getValue函数吗?

解决方法

在您的示例中,UserCatDogstructurally identical,因此TypeScript将它们视为同一类型。这可能是不可取的,所以我将给他们一些不同的结构:

class User {
    name: string;
    password: string = "hunter2";
    constructor(name: string) {
        this.name = name;
    }
}

class Dog {
    name: string;
    bark() {

    }
    constructor(name: string) {
        this.name = name;
    }
}

class Cat {
    name: string;
    meow() {

    }
    constructor(name: string) {
        this.name = name;
    }
}

现在,编译器可以区分它们。


我在这里的方法是使getValue()成为一个generic函数,其返回值为conditional type

type Settings = typeof settings[number];
type SettingsValue<K extends Settings["key"]> = Extract<Settings,{ key: K }>["value"];
type InstanceIfCtor<T> = T extends new (...args: any) => infer R ? R : T;
type SettingsOutputValue<K extends Settings["key"]> = InstanceIfCtor<SettingsValue<K>>;

declare function getValue<K extends Settings["key"]>(key: K): SettingsOutputValue<K>;

Settings类型只是{key: K,value: V}中所有settings元素类型的并集。然后,SettingsValue<K>K的{​​{1}}类型之一的key,并返回对应的Settings类型。

类型value采用类型InstanceIfCtor<T>,如果T是构造函数类型,则返回其对应的实例类型;否则返回T

然后T首先获得值类型SettingsOutputValue<K>,然后对其求值SettingsValue<K>以找到InstanceIfCtor<>应该返回的类型。如果getValue()不是K的构造函数之一,则返回类型仅为SettingsValue<K>。否则,如果SettingsValue<K> 构造函数,则SettingsValue<K>是该构造函数的实例类型。


实际上无法以编译器验证为安全的方式实施SettingsOutputValue<K>,因为它无法遵循将getValue()转换为K所需的高阶推理。未指定的SettingsOutputValue<K>。通常,我只是作为单个调用签名overload来执行此操作,以放松类型检查,请记住,我需要注意我的实现确实在执行调用签名所说的操作:

K

好的,让我们对其进行测试:

function getValue<K extends Settings["key"]>(key: K): SettingsOutputValue<K>;
function getValue(key: Settings["key"]) {
    const item = settings.find(obj => obj.key === key);
    if (item) {
        const { value } = item
        if (typeof value === 'function') {
            return new value('test value');
        } else {
            return value;
        }
    } else {
        throw new Error('not found');
    }
}

看起来不错。分别推断const dog = getValue('dog'); // Dog dog.bark(); const cat = getValue('cat'); // Cat cat.meow(); const user = getValue(User); // User user.password.toUpperCase(); dogcat的类型为userDogCat


Playground link to code

,
const settings = [
    { key: 'a',value: 'string' },{ key: 'b',value: 123 },{ key: 'c',value: true },{ key: 'dog',value: Dog },{ key: 'cat',value: Cat },{ key: Dog,{ key: Cat,{ key: User,value: User },] as const;

type SettingsKey = typeof settings[number]['key'];

function getValue(key: SettingsKey) {
    const item = settings.find(obj => obj.key === key);
    if (item) {
        const { value } = item
        if (typeof value === 'function') {
            // const value: new (name: string) => User | Dog | Cat
            return new value('test value');
        } else {
            return value;
        }
    } else {
        throw new Error('not found');
    }
}

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?