简体   繁体   English

为什么我的 Typescript object 允许这个额外的属性?

[英]Why is this extra property allowed on my Typescript object?

We've recently started using typescript for our web platform projects.我们最近开始将 typescript 用于我们的 web 平台项目。

One of the great advantages was supposed to be the powerful typing system, which allows compile-time checks of all kinds of correctness (assuming we put in the effort to model and declare our types properly).最大的优势之一应该是强大的类型系统,它允许在编译时检查各种正确性(假设我们努力 model 并正确声明我们的类型)。

Currently, I seem to have found the limits of what the type system is able to achieve, but it seems inconsistent and I may also just be using wrong syntax.目前,我似乎已经找到了类型系统能够实现的限制,但它似乎不一致,我也可能只是使用了错误的语法。

I'm trying to model the types of objects our app will be receiving from the backend, and to use the type system to have the compiler checks everywhere in the app for:我正在尝试 model 我们的应用程序将从后端接收的对象类型,并使用类型系统让编译器在应用程序的任何地方检查:

  1. structure, ie only existing (enumerated) properties are allowed by the TS compiler on objects of a type结构,即 TS 编译器只允许在某个类型的对象上存在现有(枚举)属性
  2. property typechecks, ie the type of every property is known to the TS compiler属性类型检查,即 TS 编译器知道每个属性的类型

Here is a minimized version of my approach (or take a direct link to TS playground )这是我的方法的最小化版本(或直接链接到 TS playground

interface DataObject<T extends string> {
    fields: {
        [key in T]: any   // Restrict property keys to finite set of strings
    }
}

// Enumerate type's DB field names, shall be used as constants everywhere
// Advantage: Bad DB names because of legacy project can thus be hidden in our app :))
namespace Vehicle {
    export enum Fields {
        Model = "S_MODEL",
        Size = "SIZE2"
    }
}

// CORRECT ERROR: Property "SIZE2" is missing
interface Vehicle extends DataObject<Vehicle.Fields> {
    fields: {
        [Vehicle.Fields.Model]: string,
    }
}

// CORRECT ERROR: Property "extra" is not assignable
interface Vehicle2 extends DataObject<Vehicle.Fields> {
    fields: {
        extra: string
    }
}

// NO ERROR: Property extra is now accepted!
interface Vehicle3 extends DataObject<Vehicle.Fields> {
    fields: {
        [Vehicle.Fields.Model]: string,
        [Vehicle.Fields.Size]: number,
        extra: string  // Should be disallowed!
    }
}

Why is the third interface declaration not throwing an error, when the compiler seems perfectly able to disallow the invalid property name in the second case?当编译器似乎完全能够在第二种情况下禁止无效的属性名称时,为什么第三个接口声明没有引发错误?

If you imagine that fields is an interface like so: 如果你想象那些fields是这样的接口:

interface Fields {
    Model: string;
    Size: number;
}

(It's done anonymously, but it does match this interface because of your [key in Vehicle.Fields]: any ) (它是匿名完成的,但它确实匹配此接口,因为你的[key in Vehicle.Fields]: any

Then this fails, because it doesn't match that interface - it doesn't have a Model or a Size property: 然后这会失败,因为它与该接口匹配 - 它没有ModelSize属性:

fields: {
    extra: string
}

However, this passes: 但是,这通过了:

fields: {
    Model: string;
    Size: number;
    extra: string
}

Because the anonymous interface there is an extension of your Fields interface. 因为匿名接口有Fields接口的扩展 It would look something like this: 它看起来像这样:

interface ExtendedFields extends Fields {
    extra: string;
}

This is all done anonymously through the TypeScript compiler, but you can add properties to an interface and still have it match the interface, just like an extended class is still an instance of the base class 这些都是通过TypeScript编译器匿名完成的,但您可以向接口添加属性并使其与接口匹配,就像扩展类仍然是基类的实例一样

This is the expected behavior. 这是预期的行为。 The base interface only specifies what the minimum requirements for field are, there is not requirement in typescript for an exact match between the implementing class field and the interface field. 基本接口仅指定field的最低要求,在typescript中没有要求实现类字段和接口字段之间的完全匹配。 The reason you get an error on Vehicle2 is not the presence of extra but rather that the other fields are missing. 你在Vehicle2上得到错误的原因不是extra的存在,而是缺少其他字段。 (The bottom error is Property 'S_MODEL' is missing in type '{ extra: string; }'. ) Property 'S_MODEL' is missing in type '{ extra: string; }'.底部错误“ Property 'S_MODEL' is missing in type '{ extra: string; }'.

You can do some type trickery to get an error if those extra properties are present using conditional types: 如果使用条件类型存在这些额外属性,您可以使用某种类型技巧来获取错误:

interface DataObject<T extends string, TImplementation extends { fields: any }> {
    fields: Exclude<keyof TImplementation["fields"], T> extends never ? {
        [key in T]: any   // Restrict property keys to finite set of strings
    }: "Extra fields detected in fields implementation:" & Exclude<keyof TImplementation["fields"], T>
}

// Enumerate type's DB field names, shall be used as constants everywhere
// Advantage: Bad DB names because of legacy project can thus be hidden in our app :))
namespace Vehicle {
    export enum Fields {
        Model = "S_MODEL",
        Size = "SIZE2"
    }
}

// Type '{ extra: string; [Vehicle.Fields.Model]: string; [Vehicle.Fields.Size]: number; }' is not assignable to type '"Extra fields detected in fields implementation:" & "extra"'.
interface Vehicle3 extends DataObject<Vehicle.Fields, Vehicle3> {
    fields: {
        [Vehicle.Fields.Model]: string,
        [Vehicle.Fields.Size]: number,
        extra: string // 
    }
}

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

相关问题 TypeScript:在递归类型中,只允许在对象声明中分配额外的道具 - TypeScript: in recursive type, extra props allowed to be assigned in object declaration only Typescript - 属性的允许值 - Typescript - Allowed values for a property 这是 Typescript 错误吗? 向 object 添加额外属性时出现“不可分配给类型”错误 - Is this a Typescript bug? Error “not assignable to type” when adding an extra property to an object 为什么 Typescript 允许不正确的普通 object 返回接口定义 - Why does Typescript allowed incorrect plain object return for Interface definition 为什么typescript在object找不到属性? - Why is typescript unable to find a property in the object? 为什么不能在 TypeScript 中直接访问 object 的属性? - Why is it not possible to directly access property of object in TypeScript? Typescript:如果属性值来自嵌套的 object,为什么我的代码无法编译? - Typescript: Why doesn't my code compile if a property value comes from a nested object? Typescript 属性仅在另一个属性存在时才允许 - Typescript property only allowed if another property exists 使用 Typescript 做出反应:为什么我的数组变量是一个对象? - React with Typescript: why is my array variable an object? Vue + Typescript,为什么我的计算属性没有更新我的视图 - Vue + Typescript, Why is my computed property not updating my view
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM