简体   繁体   English

有没有办法检查 Typescript 中的类型相等性?

[英]Is there a way to check for type equality in Typescript?

I'd like to get an error if two types don't match.如果两种类型不匹配,我想得到一个错误。 I have an object:我有一个对象:

const ACTIVITY_ATTRIBUTES = {
    onsite: {
        id:  "applied",
        ....
    },
    online: {
        id: "applied_online",
        ....
    },
    ...
} as const

I'd like it to be limited to the strings the server can accept我希望它仅限于服务器可以接受的字符串

export type ContactAttribute = "applied" | "applied_online" | "donated" | "messaged" | "reviewed"

I know as const can't go with a type limitation (as const will be ignored).我知道 const 不能带有类型限制(因为 const 将被忽略)。 But is there a way to to check for type equality, to enforce that the id property is of type ContactAttribute?但是有没有办法检查类型相等,强制 id 属性属于 ContactAttribute 类型? Something like:就像是:

$Values<typeof ACTIVITY_ATTRIBUTES >["id"] === ContactAttribute

You should check out this StackOverflow thread to know a easier way to achieve this.您应该查看此StackOverflow 线程以了解实现此目的的更简单方法。

Something like:就像是:

const ACTIVITY_ATTRIBUTES = {
    onsite: {
        id:  "applied",
    },
    donation: {
        id: "donated",
    },
    online: {
        id: "applied_online",
    },
    reviewe: {
        id: "reviewed",
    },
}

export type ContactAttribute = "applied" | "applied_online" | "donated" | "messaged" | "reviewed"

function isOfTypeContact (inputId: string): inputId is ContactAttribute {
    return ["applied", "applied_online", "donated", "messaged", "reviewed"].includes(inputId);
}

console.log(isOfTypeContact(ACTIVITY_ATTRIBUTES.onsite.id)) // true

Should work.应该管用。

I'd come up with a helper function that only accepts arguments whose properties have an id property of type ContactAttribte , and returns its argument untouched, and without changing its type:我想出了一个辅助函数,它只接受其属性具有ContactAttribte类型的id属性的参数,并原封不动地返回其参数,并且不更改其类型:

const hasGoodContactAttributes =
    <T extends Record<keyof T, { id: ContactAttribute }>>(t: T) => t;

Then you'd create ACTIVITY_ATTRIBUTES like this:然后你会像这样创建ACTIVITY_ATTRIBUTES

const ACTIVITY_ATTRIBUTES = hasGoodContactAttributes({
    onsite: {
        id: "applied",
        otherProp: 123,
    },
    donation: {
        id: "donated",
        alsoOtherProp: 456
        //....
    },
    online: {
        id: "applied_online",
        //....
    },
    reviewe: {
        id: "reviewed",
        //....
    },
}); // as const if you want

If you make a mistake, you'll get an error:如果你犯了一个错误,你会得到一个错误:

const BAD_ATTRIBUTES = hasGoodContactAttributes({
    okay: {
        id: "messaged"
    },
    oops: {
        id: "reviewed_online" // error!
    //  ~~ <-- '"reviewed_online"' is not assignable to type 'ContactAttribute'
    }
})

Okay, hope that helps;好的,希望有帮助; good luck!祝你好运!

Link to code 代码链接

This could be achieved using a dummy validating function.这可以使用虚拟验证功能来实现。

const validateType = <T> (obj:T) => undefined 

All that is left is to call it with the type and object:剩下的就是用类型和对象调用它:

type ContactAttribute = "applied" | "applied_online" | "donated" | "messaged" | "reviewed"

type ActivityAttributes= {
   [k:string]: {
      id: ContactAttribute 
   }
}

const ACTIVITY_ATTRIBUTES = {
    onsite: {
        id:  "applied",
        ....
    },
    donation: {
        id: "donated",
        ....
    },
    ...
} as const

validateType<ActivityAttributes>(ACTIVITY_ATTRIBUTES) // Will show an error if types don't match. 

You can actually achieve that by using the following code:您实际上可以通过使用以下代码来实现:

const ACTIVITY_ATTRIBUTES: {[key: string]: {id: ContactAttribute}} = {
    onsite: {
        id:  "applied",
    },
    donation: {
        id: "donated",
    },
    online: {
        id: "applied_online",
    },
    reviewe: {
        id: "reviewed",
    },
    hi: {
      id: 'applied',
    }
} as const

type ContactAttribute = "applied" | "applied_online" | "donated" | "messaged" | "reviewed"

However, I suspect this might want more typechecking than this, because with the solution above, even the id can be checked properly, you still can have falsy property placed for different id .但是,我怀疑这可能需要比这更多的类型检查,因为使用上面的解决方案,即使可以正确检查id ,您仍然可以为不同的id放置虚假属性。

To avoid this issue, you might want to use discriminated union .为避免此问题,您可能需要使用discriminated union

For example:例如:

type Contact = {
  id: "applied",
  x: string
} | {
  id: "applied_online"
  y: number
}

By leveraging this feature, you can make sure that the expected properties associated with each ContactAttribute would be initialised correctly.通过利用此功能,您可以确保正确初始化与每个ContactAttribute关联的预期属性。

You can achieve this by using a combination of declare and dead code.您可以通过使用declare和死代码的组合来实现这一点。 The declare statements are purely compile time so we use those to set up fake "values" of our types. declare语句纯粹是编译时,所以我们使用它们来设置我们类型的假“值”。 Then we use a dead code block to call a function that takes two parameters of the same type.然后我们使用一个死代码块来调用一个接受两个相同类型参数的函数。

type T1 = number;
type T2 = string;

declare const dummy1: T1;
declare const dummy2: T2;
declare function sameType<T>(v1: T, v2: T): void;
if (false) {
    // @ts-ignore: Unreachable code
    sameType(
        // prevent line break so that the ts-ignore is only applied to the above line
        dummy1, dummy2);
}

The @ts-ignore is to silence the unreachable code error. @ts-ignore是为了消除无法访问的代码错误。

I added a line comment after ( to prevent tools like prettier from putting the arguments on the same line which would silence the error you want to achieve.我在(之后添加了一行注释,以防止 prettier 之类的工具将参数放在同一行上,这将使您想要实现的错误静音。

You can also use an as expression which might be a little bit more concise.您还可以使用as表达式,这可能会更简洁一些。

type T1 = number;
type T2 = string;

declare const dummy1: T1;
if (false) {
    // @ts-ignore: Unreachable code
    dummy1 
        // prevent line break so that the ts-ignore is only applied to the above line
        as T2
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM