Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
是否可以限制typescript对象仅包含其类定义的属性?_Typescript - Fatal编程技术网

是否可以限制typescript对象仅包含其类定义的属性?

是否可以限制typescript对象仅包含其类定义的属性?,typescript,Typescript,这是我的密码 async getAll(): Promise<GetAllUserData[]> { return await dbQuery(); // dbQuery returns User[] } class User { id: number; name: string; } class GetAllUserData{ id: number; } async getAll():Promise{ return wait dbQuery()

这是我的密码

async getAll(): Promise<GetAllUserData[]> {
    return await dbQuery(); // dbQuery returns User[]
}

class User {
    id: number;
    name: string;
}

class GetAllUserData{
    id: number;
}
async getAll():Promise{
return wait dbQuery();//dbQuery返回用户[]
}
类用户{
id:编号;
名称:字符串;
}
类GetAllUserData{
id:编号;
}
getAll
函数返回
User[]
,数组的每个元素都有
name
属性,即使其返回类型是
GetAllUserData[]


我想知道,在typescript中,是否有可能将对象限制为仅由其类型指定的属性。

我认为使用您现有的代码结构是不可能的。Typescript确实有,这听起来像您所追求的,但它们只适用于对象文本。从这些文件中:

对象文字得到特殊处理,并在将其分配给其他变量或作为参数传递时进行多余的属性检查

但返回的变量将不会接受该检查。那么一会儿

function returnUserData(): GetAllUserData {
    return {id: 1, name: "John Doe"};
}
将产生错误“对象文字可能仅指定已知属性”,代码:

function returnUserData(): GetAllUserData {
    const user = {id: 1, name: "John Doe"};
    return user;
}
不会产生任何错误,因为它返回的是变量,而不是对象文本本身

因此,对于您的情况,由于
getAll
不返回文本,因此typescript不会执行多余的属性检查

最后一点注意:有一个定义,如果实现了它,将允许您在这里进行所需的检查。

这意味着类型定义实际上只是该类型对象的“形状”。它还意味着,任何共享另一个类型的“形状”子集的类型都隐式地是该类型的子类

在您的示例中,由于
用户
具有
GetAllUserData
的所有属性,
用户
隐式地是
GetAllUserData
的子类型

为了解决这个问题,可以专门添加一个虚拟属性,使两个类彼此不同。这种类型的属性称为鉴别器。(搜索受歧视的工会)

您的代码可能如下所示。鉴别器属性的名称并不重要。这样做将产生您想要的类型检查错误

async function getAll(): Promise<GetAllUserData[]> {
  return await dbQuery(); // dbQuery returns User[]
}

class User {
  discriminator: 'User';
  id: number;
  name: string;
}

class GetAllUserData {
  discriminator: 'GetAllUserData';
  id: number;
}
异步函数getAll():Promise{ return wait dbQuery();//dbQuery返回用户[] } 类用户{ 鉴别器:“用户”; id:编号; 名称:字符串; } 类GetAllUserData{ 鉴别器:“GetAllUserData”; id:编号; }
带有鉴别器的公认答案是正确的。TypeScript使用结构类型而不是名义类型。这意味着transpiler将检查结构是否匹配。由于这两个类(可以是接口或类型)都有类型
id
number
匹配,因此可以互换(这是真实的,因为
User
具有更多属性

虽然这可能已经足够好了,但问题是在运行时,从方法
getAll
返回的数据将包含
name
属性。返回更多可能不是问题,但如果您将信息发回其他地方,则可能是问题

如果要将数据限制为仅在类(接口或类型)中定义的数据,则必须手动构建或扩展新对象。下面是如何查找示例:

function dbQuery(): User[] {
    return [];
}
function getAll(): GetAllUserData[] {
    const users: User[] = dbQuery();
    const usersIDs: GetAllUserData[] = users.map(({id}) => ({id}));
    return usersIDs;
}

class User {
    id: number;
    name: string;
}

class GetAllUserData {
    id: number;
}
如果不使用修剪字段的运行时方法,您可以向TypeScript指示这两个类与私有字段不同。当返回类型设置为
GetAllUserData
时,下面的代码不允许您返回
User

class User {

    id: number;
    name: string;
}

class GetAllUserData {
    private _unique: void;
    id: number;
}
function getAll(): GetAllUserData[] {
    return dbQuery(); // Doesn't compile here!
}

我找到了一种方法,使用自TypeScript版本3以来可用的内置类型,以确保传递给函数的对象不包含任何超出指定(对象)类型的属性

//首先,定义一个类型,当传递键的并集时,该类型将创建一个
//无法具有这些属性。我找不到直接使用此类型的方法,
//但它可以与以下类型一起使用。
类型不可能={
[P in K]:从不;
};
//秘方!为其提供仅包含所需属性的类型,
//然后是一个扩展该类型的类型,基于调用方提供的内容
//使用泛型。
类型NOEXTRAPPROPERTYS=U&不可能;
//现在让我们试试看!
//使用的简单类型
界面动物{
名称:字符串;
噪音:弦;
}
//这是可行的,但我同意这种类型很粗俗。但这可能会使它更容易
//看看这是怎么回事。
//
//传递给函数的任何内容都必须至少满足动物契约
//(部分),但我们会相交于任何类型
//一种不可能的类型,上面只有动物身上不存在的钥匙。
//结果是,在动物身上不存在的键有一种“从不”类型,
//因此,如果它们存在,就会被标记为错误!
功能thisWorks(动物:T&不可能):无效{
log(${animal.name.toLowerCase()}发出的噪音是${animal.noise}.`);
}
//使用上面的NoExtraProperty类型,这是我可以将其简化为的最好的方法。
//使用此技术的函数都需要遵循此公式。
功能Thisisagoodasicangetit(动物:无额外财产):无效{
log(${animal.name.toLowerCase()}发出的噪音是${animal.noise}.`);
}
//它适用于定义为类型的变量
常数OK:NoExtraProperties={
名字:“狗”,
噪音:“吠声”,
};
常数错误1:NoExtraProperty={
名字:'猫',
噪音:“喵”
BetterHandgs:false,//看,一个错误!
};
//如果我们试图绕过对对象文本所做的“多余属性检查”,会发生什么
//通过将其分配给没有显式类型的变量?
常数错误2={
名称:'老鼠',
噪音:“吱吱”声,
理想场景:[“实验室”、“仓库”],
无效:对,
};
这很有效(好);
thisWorks(错误1);//此处不将其标记为错误,但在上方将其标记为错误
这很有效(错误2);//耶,一个错误!
这是一个很好的例子;
这是斯戈达西坎蒂
type Impossible<K extends keyof any> = {
  [P in K]: never;
};

export type NoExtraProperties<T, U extends T = T> = U extends Array<infer V>
  ? NoExtraProperties<V>[]
  : U & Impossible<Exclude<keyof U, keyof T>>;
// From GregL's answer

type Impossible<K extends keyof any> = {
    [P in K]: never;
 };

 type NoExtraProperties<T, U extends T = T> = U & Impossible<Exclude<keyof U, keyof T>>;

 interface Animal {
    name: string;
    noise: string;
 }

 function thisWorks<T extends Animal>(animal: T & Impossible<Exclude<keyof T, keyof Animal>>): void {
    console.log(`The noise that ${animal.name.toLowerCase()}s make is ${animal.noise}.`);
 }

 function thisIsAsGoodAsICanGetIt<T extends Animal>(animal: NoExtraProperties<Animal, T>): void {
    console.log(`The noise that ${animal.name.toLowerCase()}s make is ${animal.noise}.`);
 }

 const wrong2 = {
    name: 'Rat',
    noise: 'squeak',
    idealScenarios: ['labs', 'storehouses'],
    invalid: true,
 };

 thisWorks(wrong2); // yay, an error!

 thisIsAsGoodAsICanGetIt(wrong2); // yay, an error!
const fun = (animal:Animal) =>{
    thisWorks(animal) // No Error
    thisIsAsGoodAsICanGetIt(animal) // No Error
}

fun(wrong2) // No Error
interface Narrow {
   a: "alpha"
}

interface Wide extends Narrow{
   b: "beta" 
}

const fun = (obj: Narrow) => {
   const narrowKeys = ["a"]
   const narrow = pick(obj, narrowKeys) 
   // Even if obj has extra properties, we know for sure that narrow doesn't
   ...
}