พิมพ์คำจำกัดความในวัตถุตามตัวอักษรใน TypeScript


346

ในคลาส typescript เป็นไปได้ที่จะประกาศประเภทของคุณสมบัติเช่น:

class className {
  property: string;
};

ประกาศประเภทของคุณสมบัติในวัตถุตามตัวอักษรได้อย่างไร

ฉันลองใช้รหัสต่อไปนี้แล้ว แต่ไม่ได้คอมไพล์:

var obj = {
  property: string;
};

ฉันได้รับข้อผิดพลาดต่อไปนี้:

ชื่อ 'สตริง' ไม่มีอยู่ในขอบเขตปัจจุบัน

ฉันกำลังทำสิ่งผิดปกติหรือเป็นข้อผิดพลาดหรือไม่?

คำตอบ:


424

คุณกำลังสวยใกล้เคียงคุณก็ต้องเปลี่ยนด้วย= :คุณสามารถใช้ตัวอักษรประเภทวัตถุ (ดูมาตรา 3.5.3) หรืออินเทอร์เฟซ การใช้ตัวอักษรประเภทวัตถุใกล้เคียงกับสิ่งที่คุณมี:

var obj: { property: string; } = { property: "foo" };

แต่คุณยังสามารถใช้อินเทอร์เฟซ

interface MyObjLayout {
    property: string;
}

var obj: MyObjLayout = { property: "foo" };

14
ตัวเลือก DRY (Don't Repeat Yourself) โดย @Rick Love กับผู้ดำเนินรายการ เพียงแสดงความคิดเห็นไม่ downvoting ...
spechter

ฉันรู้ว่าสิ่งนี้ได้รับคำตอบเมื่อไม่นานมานี้ แต่ฉันเข้าใจผิดในข้อสันนิษฐานว่าปัญหาสามารถเกิดขึ้นได้เมื่อชั้นเรียนมีวิธีการและคุณสมบัติ เนื่องจากคลาสไม่ได้รับการเตรียมใช้งานและเรากำหนดคุณสมบัติเท่านั้นการเรียกเมธอดในคลาสจะทำให้เกิดข้อยกเว้นแบบ null โดยทั่วไปแล้ววัตถุที่เราสร้างเพียง 'การกระทำ' เป็นคลาสเพราะเรากำหนดประเภทของมัน แต่จริงๆแล้วมันไม่ได้เป็นตัวอย่างของคลาสนั้น นั่นคือเราต้องสร้างชั้นเรียนด้วยคำหลัก 'ใหม่' แล้วเราก็กลับไปที่จตุรัส 1 เนื่องจากเราไม่สามารถทำอะไรเช่นคลาสใหม่ () {prop: 1}; ใน TS อย่างที่เราสามารถทำได้ใน C # เช่น
DeuxAlpha

@DeuxAlpha นี่คือการกำหนดให้กับอินเทอร์เฟซซึ่งไม่มีวิธีการ ฉันไม่เชื่อว่าคุณสามารถมอบหมายให้ชั้นเรียนเช่นนี้
Mog0

เชื่อมโยงไปยังข้อมูลจำเพาะจะดี :)
Ahong

@DeuxAlpha นี่เป็นการสร้างวัตถุตามตัวอักษรไม่ใช่วัตถุคลาส อินเตอร์เฟซสามารถมีวิธีการ หากอินเทอร์เฟซของคุณกำหนดวิธีการวัตถุตัวอักษรต้องกำหนดด้วย - มันจะไม่เป็นโมฆะ ตัวอักษรของวัตถุจะต้องตอบสนองทุกอย่างที่อินเตอร์เฟสกำหนดไว้หรือระบบประเภทจะแสดงข้อผิดพลาด
Rick Love

291

อัปเดต 2019-05-15 (ปรับปรุงรูปแบบรหัสเป็นทางเลือก)

หลังจากใช้งานมาหลายปีconstและได้รับประโยชน์จากโค้ดที่ใช้งานได้มากขึ้นผมขอแนะนำให้ใช้ต่อไปนี้ในกรณีส่วนใหญ่ (เมื่อสร้างวัตถุการบังคับให้ระบบประเภทเป็นประเภทเฉพาะแทนที่จะปล่อยให้มันอนุมานประเภทมักจะบ่งชี้ว่ามีบางอย่างผิดปกติ)

แต่ฉันขอแนะนำให้ใช้constตัวแปรให้มากที่สุดแล้วเขียนวัตถุเป็นขั้นตอนสุดท้าย:

const id = GetId();
const hasStarted = true;
...
const hasFinished = false;
...
return {hasStarted, hasFinished, id};
  • สิ่งนี้จะพิมพ์ทุกอย่างอย่างเหมาะสมโดยไม่จำเป็นต้องพิมพ์อย่างชัดเจน
  • ไม่จำเป็นต้องพิมพ์ชื่อฟิลด์อีกครั้ง
  • สิ่งนี้นำไปสู่รหัสที่สะอาดที่สุดจากประสบการณ์ของฉัน
  • สิ่งนี้ทำให้คอมไพเลอร์ให้การตรวจสอบสถานะมากขึ้น (ตัวอย่างเช่นหากคุณกลับมาในหลาย ๆ สถานที่ผู้รวบรวมจะให้แน่ใจว่าวัตถุชนิดเดียวกันนั้นจะถูกส่งคืนเสมอ - ซึ่งกระตุ้นให้คุณประกาศมูลค่าการส่งคืนทั้งหมดในแต่ละตำแหน่ง เจตนาของมูลค่านั้น)

เพิ่มเติม 2020-02-26

หากคุณต้องการชนิดที่คุณสามารถเริ่มต้นได้อย่างเกียจคร้าน: ทำเครื่องหมายว่าเป็นประเภทยูเนี่ยนที่สามารถยกเลิกได้ (โมฆะหรือประเภท) ระบบประเภทจะป้องกันคุณจากการใช้โดยไม่ต้องแน่ใจว่ามีค่า

ในtsconfig.jsonตรวจสอบให้แน่ใจว่าคุณเปิดใช้งานการตรวจสอบ null อย่างเข้มงวด:

"strictNullChecks": true

จากนั้นใช้รูปแบบนี้และอนุญาตให้ระบบประเภทปกป้องคุณจากการเข้าใช้ null / ไม่ได้กำหนดโดยไม่ตั้งใจ:



const state = {
    instance: null as null | ApiService,
    // OR
    // instance: undefined as undefined | ApiService,

};

const useApi = () => {
    // If I try to use it here, the type system requires a safe way to access it

    // Simple lazy-initialization 
    const api = state?.instance ?? (state.instance = new ApiService());
    api.fun();

    // Also here are some ways to only access it if it has value:

    // The 'right' way: Typescript 3.7 required
    state.instance?.fun();

    // Or the old way: If you are stuck before Typescript 3.7
    state.instance && state.instance.fun();

    // Or the long winded way because the above just feels weird
    if (state.instance) { state.instance.fun(); }

    // Or the I came from C and can't check for nulls like they are booleans way
    if (state.instance != null) { state.instance.fun(); }

    // Or the I came from C and can't check for nulls like they are booleans 
    // AND I was told to always use triple === in javascript even with null checks way
    if (state.instance !== null && state.instance !== undefined) { state.instance.fun(); }
};

class ApiService {
    fun() {
        // Do something useful here
    }
}

อย่าทำด้านล่างใน 99% ของกรณี:

อัพเดท 2016-02-10 - จัดการ TSX (ขอบคุณ @Josh)

ใช้asโอเปอเรเตอร์สำหรับ TSX

var obj = {
    property: null as string
};

ตัวอย่างที่ยาวกว่า:

var call = {
    hasStarted: null as boolean,
    hasFinished: null as boolean,
    id: null as number,
};

คำตอบเดิม

ใช้ตัวดำเนินการร่ายเพื่อทำให้รวบรัดนี้ (โดยการร่ายโมฆะเป็นประเภทที่ต้องการ)

var obj = {
    property: <string> null
};

ตัวอย่างที่ยาวกว่า:

var call = {
    hasStarted: <boolean> null,
    hasFinished: <boolean> null,
    id: <number> null,
};

สิ่งนี้ดีกว่าการมีสองส่วน (หนึ่งเพื่อประกาศประเภทที่สองเพื่อประกาศค่าเริ่มต้น):

var callVerbose: {
    hasStarted: boolean;
    hasFinished: boolean;
    id: number;
} = {
    hasStarted: null,
    hasFinished: null,
    id: null,
};

10
หากคุณใช้ TSX (TS กับ JSX) คุณไม่สามารถใช้การตั้งชื่อวงเล็บมุมได้ดังนั้นเส้นเหล่านั้นจะกลายเป็นสิ่งที่property: null as stringขัดแย้งกับasผู้ปฏิบัติงาน
Josh

2
@RickLove สิ่งนี้จริง ๆ แล้ว จำกัด ประเภทของตัวแปรวัตถุหรือเป็นวิธีเดียวที่จะระบุชนิดเมื่อกำหนด? ในคำอื่น ๆ หลังจากที่คุณกำหนดให้กับตัวแปรcallในตัวอย่างที่สองของคุณคุณสามารถกำหนดประเภทที่แตกต่างอย่างสิ้นเชิงให้กับมันได้หรือไม่
Daniel Griscom

3
นี่ควรเป็นคำตอบ
Drew R

4
ไม่ทำงาน. Error:(33, 15) TS2352:Type 'null' cannot be converted to type 'string'.
slideshowp2

2
นี่เป็นเพียงการใช้ภาษาในทางที่ผิดหรือนี่เป็นเรื่องจริงหรือไม่? คุณสามารถให้ลิงค์สำหรับหมออ่านไปยังเอกสารทางการได้หรือไม่? ขอบคุณ!
Qwerty

31

ฉันประหลาดใจที่ไม่มีใครพูดถึงเรื่องนี้ แต่คุณสามารถสร้างอินเทอร์เฟซที่เรียกObjectLiteralว่ารับkey: valueประเภทคู่ได้string: any:

interface ObjectLiteral {
  [key: string]: any;
}

จากนั้นคุณจะใช้มันเช่นนี้

let data: ObjectLiteral = {
  hello: "world",
  goodbye: 1,
  // ...
};

โบนัสเพิ่มเติมคือคุณสามารถใช้อินเทอร์เฟซนี้ซ้ำได้หลายครั้งตามที่คุณต้องการบนวัตถุจำนวนมากที่คุณต้องการ

โชคดี.


3
นี่เป็นความคิดที่ไม่ดีมันทำให้ระบบประเภทไร้ค่า จุดของ typescript คือการอนุญาตให้ระบบประเภทเพื่อช่วยป้องกันข้อผิดพลาดและยังช่วยให้ฟังก์ชันการเติมข้อความอัตโนมัติที่ดีขึ้นในการใช้เครื่องมือ - โดยทั่วไปจะปิดการใช้งานประโยชน์ทั้งหมดของ typescript มันจะเป็นการดีกว่าถ้าคุณไม่ใช้อินเตอร์เฟสใด ๆ ในตัวอย่างด้านบน
Rick Love

1
@ RickLove ฉันไม่เห็นด้วยอย่างยิ่ง สิ่งนี้มีประโยชน์เป็นพิเศษเมื่อคุณสมบัติหลายอย่างเป็นทางเลือก แต่เรายังต้องการกำหนดประเภททั้งหมดอย่างชัดเจนภายใน (เช่นมีอาร์กิวเมนต์สำหรับฟังก์ชัน) ซึ่งอาจจะถูกมองว่าเป็นเพียงน้ำตาลประโยคและตัวอักษรทำงานเหมือนผู้ประกอบการแพร่กระจายสำหรับคุณสมบัติการเชื่อมต่อ
CPHPython

@CPHPython ทำไมไม่เพียง แต่ระบุพารามิเตอร์ทางเลือกด้วยประเภทเฉพาะของพวกเขา ครั้งเดียวนี้จะสมเหตุสมผลถ้าชื่อไม่รู้จักในเวลารหัส (เช่นพวกเขามาจากฐานข้อมูลหรือแหล่งภายนอก) นอกจากนี้สำหรับการรวมกันที่ซับซ้อนของการขัดแย้งประเภทสหภาพทำงานได้ดี หากคุณกำลังเข้ารหัสกับชื่อเฉพาะพวกเขาควรจะกำหนดถ้าเป็นไปได้ หากเป็นเพียงข้อมูลที่ไม่ส่งผลกระทบต่อลอจิกแน่นอนปล่อยให้มันออกจากระบบประเภท
Rick Love

1
@RickLove "ชื่อไม่เป็นที่รู้จักในเวลารหัส" -> นี่เป็นตัวอย่างที่ดีและใช้งานได้จริงค่าของประเภทคีย์เหล่านั้นเป็นที่รู้จักและเป็นเหมือนกันทั้งหมด (เช่นสตริง ) ใจว่าฉันสนับสนุนการใช้งานชิ้น[key: string]ส่วนเท่านั้นไม่ใช่ส่วนใด ๆ ที่เป็นนิยามประเภทของค่า ... นั่นก็คือการใช้ประโยชน์จากประเภทนั้น ๆ
CPHPython

1
ฉันจะทำอย่างไรถ้าฉันต้องการอนุญาตให้เพิ่มสตริงและตัวเลข แต่ไม่ใช่ประเภทอื่น ๆ
DonkeyBanana

14

หากคุณพยายามที่จะเขียนคำอธิบายประกอบประเภทไวยากรณ์คือ:

var x: { property: string; } = ...;

หากคุณพยายามที่จะเขียนตัวอักษรวัตถุไวยากรณ์คือ:

var x = { property: 'hello' };

รหัสของคุณพยายามใช้ชื่อประเภทในตำแหน่งค่า


10
คุณvar x: { property: string; } = ...;คือแซว! var x: { property: string; } = { property: 'hello' };ฉันหวังว่าจุดไข่ปลาเป็นไวยากรณ์ที่ถูกต้องจะเป็นชวเลข
Jason Kleban

10

ใน TypeScript ถ้าเราประกาศวัตถุแล้วเราจะใช้ไวยากรณ์ต่อไปนี้:

[access modifier] variable name : { /* structure of object */ }

ตัวอย่างเช่น:

private Object:{ Key1: string, Key2: number }

7

หากคุณกำลังพยายามเพิ่มการพิมพ์ลงในวัตถุที่ถูกทำลายตัวอย่างเช่นขัดแย้งกับฟังก์ชันไวยากรณ์คือ

function foo({ bar, baz }: { bar: boolean, baz: string }) {
  // ...
}

foo({ bar: true, baz: 'lorem ipsum' });

5

หากคุณสมบัติของคุณมีประเภทเดียวกันคุณสามารถใช้ประเภทยูทิลิตี้ที่กำหนดไว้ล่วงหน้าRecord:

type Keys = "property" | "property2"

var obj: Record<Keys, string> = {
  property: "my first prop",
  property2: "my second prop",
};

แน่นอนคุณสามารถไปต่อและกำหนดประเภทที่กำหนดเองสำหรับค่าคุณสมบัติของคุณ:

type Keys = "property" | "property2"
type Value = "my prop" | "my other allowed prop"

var obj: Record<Keys, Value> = {
  property: "my prop",
  property2: "my second prop", // TS Error: Type '"my second prop"' is not assignable to type 'Value'.
};

2
สิ่งที่ฉันต้องการ ขอบคุณ! typescript สำหรับผู้ชนะ!
Audacitus

ฉันจะใช้letแทนที่varนี่
Fwd079

3
// Use ..

const Per = {
  name: 'HAMZA',
  age: 20,
  coords: {
    tele: '09',
    lan: '190'
  },
  setAge(age: Number): void {
    this.age = age;
  },
  getAge(): Number {
    return age;
  }
};
const { age, name }: { age: Number; name: String } = Per;
const {
  coords: { tele, lan }
}: { coords: { tele: String; lan: String } } = Per;

console.log(Per.getAge());

3
เฮ้และยินดีต้อนรับสู่ SO! คุณช่วยขยายคำตอบของคุณได้ทั้งหมดมันจะเป็นประโยชน์ในการอธิบายว่า / ทำไมมันถึงใช้ได้
ปาร์ตี้ริง

1

ในรหัสของคุณ:

var obj = {
  myProp: string;
};

คุณกำลังสร้างวัตถุตามตัวอักษรและกำหนดสตริงตัวแปรให้กับคุณสมบัติ myProp แม้ว่าการปฏิบัติที่เลวร้ายมากนี่เป็นรหัส TS ที่ถูกต้อง (อย่าใช้สิ่งนี้!):

var string = 'A string';

var obj = {
  property: string
};

อย่างไรก็ตามสิ่งที่คุณต้องการก็คือพิมพ์ตัวอักษรของวัตถุ สามารถทำได้หลายวิธี:

อินเตอร์เฟซ:

interface myObj {
    property: string;
}

var obj: myObj = { property: "My string" };

ประเภทที่กำหนดเอง:

type myObjType = {
    property: string
};

var obj: myObjType = { property: "My string" };

ผู้ประกอบการหล่อ:

var obj = {
    myString: <string> 'hi',
    myNumber: <number> 132,
};

ประเภทวัตถุที่แท้จริง:

var obj: { property: string; } = { property: "Mystring" };
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.