Typescript arayüzü varsayılan değerleri


137

TypeScript'te aşağıdaki arayüze sahibim:

interface IX {
    a: string,
    b: any,
    c: AnotherType
}

Bu türden bir değişken bildiriyorum ve tüm özellikleri başlatıyorum

let x: IX = {
    a: 'abc',
    b: null,
    c: null
}

Daha sonra bir init işlevinde onlara gerçek değerler atarım.

x.a = 'xyz'
x.b = 123
x.c = new AnotherType()

Ancak daha sonra gerçek değerlere ayarlanacakları zaman nesneyi bildirirken her özellik için bir dizi varsayılan boş değer belirtmek zorunda kalmaktan hoşlanmıyorum. Arayüze null olarak sağlamadığım özellikleri varsayılan yapmasını söyleyebilir miyim? Bunu yapmama ne izin verirdi:

let x: IX = {
    a: 'abc'
}

derleyici hatası almadan. Şu anda bana söylüyor

TS2322: '{}' tipi, 'IX' tipine atanamaz. '{}' Türünde 'b' özelliği eksik.



3
IMO, stackoverflow.com/a/35074490/129196 yanıtı , benimsenmesi gereken yaklaşım olmamalıdır. Tüm özellikleri başlatılmadan bir durumda bir nesneye sahip olabilirseniz ve yine de geçerliyse, bu özellikleri şu yanıtta belirtildiği gibi isteğe bağlı olarak bildirmelisiniz: stackoverflow.com/a/43226857/129196 . Aksi takdirde, daktilo kullanma amacını (tür güvenliği için) kaybedeceğiz.
Charles Prakash Dasari

Yanıtlar:


99

Arayüze null olarak sağlamadığım özellikleri varsayılan yapmasını söyleyebilir miyim? Bunu yapmama ne izin verirdi

Hayır. Ancak varsayılan olarak bunlar undefinedçoğunlukla iyidir. Aşağıdaki kalıbı kullanabilirsiniz, yani yaratma noktasında bir tip iddiasına sahip olabilirsiniz:

let x: IX = {} as any;

x.a = 'xyz'
x.b = 123
x.c = new AnotherType()

Bunu ve diğer kalıpları burada belgeledim: https://basarat.gitbook.io/typescript/main-1/lazyobjectliteralinitialization


Teşekkürler, bir grup varsayılan değeri tanımlamadan tanımsız mülk için değer ayarlayamıyordum
Omar Isaid

43
Kullanılması anybaltalıyor amacını Tipi script. Bu dezavantajı olmayan başka cevaplar da var.
Jack Miller

herhangi biri benzer soru hakkında bana yardımcı olabilir ama jenerik kullanarak. İşte bu soruda
TAB

2
Sağlanan bağlantıda, 'Foo olarak' let foo = {} ile çok daha iyi bir seçenek sunduğunda basarat'ın 'herhangi bir' örneğe gitmesi gariptir; ('Foo "bir Arayüz olmak)
Neurothustra

1
OP, bir TypeScript arayüzü oluşturma zahmetine girdi ve kazan kaplaması için çözümler istiyor. Çözümünüz arayüzlerden tamamen vazgeçmek mi? TypeScript'in de atlanmasını önerebilirsin ...
Moss Palmer

98

Bir arabirimde varsayılan değerleri ayarlayamazsınız, ancak yapmak istediğinizi İsteğe Bağlı Özellikleri kullanarak gerçekleştirebilirsiniz (3. paragrafı karşılaştırın):

https://www.typescriptlang.org/docs/handbook/interfaces.html

Arayüzü şu şekilde değiştirmeniz yeterlidir:

interface IX {
    a: string,
    b?: any,
    c?: AnotherType
}

Daha sonra şunları yapabilirsiniz:

let x: IX = {
    a: 'abc'
}

Ve atamak varsayılan değerlerine İnit işlevini kullanın x.bve x.cbu properies ayarlanmış değilse.


3
Soruda başlaması x.bve x.cile yapması istendi null. Yazarken let x = {a: 'abc'}sonra x.bve x.cvardır undefinedbir akıllı hızlı bir düzeltme olsa, bu nedenle bu cevap tam gereksinimlerini karşılamıyor.
Benny Neugebauer

1
@BennyNeugebauer Kabul edilen cevap aynı kusura sahip. Bu en iyi cevap
tel

1
x varsayılan değerlere sahip bir nesneyse, son örnek nesnesi oluşturmak let a: IX = Object.assign({b:true}, x);, b, c'nin örnek nesnede de isteğe bağlı olmasına neden olur ve bu istenmeyebilir
Luckylooke

Arayüzü kullananların rahatlığı için özellikler "isteğe bağlı" olarak işaretlenmemelidir. Cevap "hayır", arabirime varsayılanları sağlamasını söyleyemezsiniz ", ancak bir arabirimin bir örneğini başlatmak için bir fabrika yöntemi sağlayabilirsiniz.
Rick O'Shea

36

@ Timar'ın cevabı nullvarsayılan değerler için mükemmel bir şekilde çalışsa da (ne isteniyordu), burada diğer varsayılan değerlere izin veren başka bir kolay çözüm: Varsayılanları içeren uygun bir sabitin yanı sıra bir seçenek arabirimi tanımlayın; yapıcıda üye değişkenini ayarlamak için yayma operatörünü kullanınoptions

interface IXOptions {
    a?: string,
    b?: any,
    c?: number
}

const XDefaults: IXOptions = {
    a: "default",
    b: null,
    c: 1
}

export class ClassX {
    private options: IXOptions;

    constructor(XOptions: IXOptions) {
        this.options = { ...XDefaults, ...XOptions };
    }

    public printOptions(): void {
        console.log(this.options.a);
        console.log(this.options.b);
        console.log(this.options.c);
    }
}

Şimdi sınıfı şu şekilde kullanabilirsiniz:

const x = new ClassX({ a: "set" });
x.printOptions();

Çıktı:

set
null
1

Bu, her durum için varsayılan bir çözüm olarak kullanılamaz, ancak bazı durumlarda kesinlikle yaratıcı bir çözümdür. 👍
abhay tripathi

ya sadece değerleri günlüğe kaydetmek yerine onları const calcvalue = this.options.a * 1000 gibi kullanmak istersem; potansiyel olarak tanımlanmamış olabileceğinden bu yine de alarm verir ..
Manish Rawat

Evet, bir sınıfı kullanabilir veya başka bir dilde yazabilirsiniz, ikisi de sorusunu cevaplamaz.
Rick O'Shea

22

Arabirimi bir sınıfla uygulayabilir, ardından yapıcıdaki üyeleri başlatmakla ilgilenebilirsiniz:

class IXClass implements IX {
    a: string;
    b: any;
    c: AnotherType;

    constructor(obj: IX);
    constructor(a: string, b: any, c: AnotherType);
    constructor() {
        if (arguments.length == 1) {
            this.a = arguments[0].a;
            this.b = arguments[0].b;
            this.c = arguments[0].c;
        } else {
            this.a = arguments[0];
            this.b = arguments[1];
            this.c = arguments[2];
        }
    }
}

Diğer bir yaklaşım, bir fabrika işlevi kullanmaktır:

function ixFactory(a: string, b: any, c: AnotherType): IX {
    return {
        a: a,
        b: b,
        c: c
    }
}

O zaman basitçe şunları yapabilirsiniz:

var ix: IX = null;
...

ix = new IXClass(...);
// or
ix = ixFactory(...);

4

PartialEşlenmiş türü belgelerde açıklandığı gibi kullanabilirsiniz : https://www.typescriptlang.org/docs/handbook/release-notes/typescript-2-1.html

Örneğinizde şunlara sahip olacaksınız:

interface IX {
    a: string;
    b: any;
    c: AnotherType;
}

let x: Partial<IX> = {
    a: 'abc'
}

x varsayılan değerlere sahip bir nesneyse, son örnek nesnesi oluşturmak let a: IX = Object.assign({b:true}, x);hataya neden olurPartial<IX> cannot be assigned to IX
Luckylooke

Kısmi, türü değiştirir. xartık uygulamıyor IX, ancak bir kısmı IX. Partialher özelliğin isteğe bağlı olabileceği yerler için iyidir; örneğin, bir nesne arayüzünün bir kısmını geçirebileceğiniz ve yalnızca tanımlanan alanları güncelleyebileceğiniz (a'nın undefinedher alanının Partialolabileceğinin aksine) ORM ile . Varsayılan değerlere sahip alanlara sahip arabirimler için, let x: Partial<IX> = { /* non-default fields */ } as IXsözdizimini kullanarak varsayılan değerleri bildirmek zorunda kalmadan bu arabirim türlerini uygulayan nesne değişmezlerini bildirebilirsiniz .
Paul-Sebastian Manole

1

Vardığımdan daha iyi bir yol ararken bunu tökezledim. Cevapları okuyup denedikten sonra, diğer cevaplar benim için özlü hissetmediğinden, yaptığım şeyi göndermeye değer olduğunu düşündüm. Her yeni arayüz kurduğumda sadece kısa bir miktar kod yazmak benim için önemliydi. Karar verdim ...

Özel bir genel deepCopy işlevi kullanma:

deepCopy = <T extends {}>(input: any): T => {
  return JSON.parse(JSON.stringify(input));
};

Arayüzünüzü tanımlayın

interface IX {
    a: string;
    b: any;
    c: AnotherType;
}

... ve varsayılanları ayrı bir sabit olarak tanımlayın.

const XDef : IX = {
    a: '',
    b: null,
    c: null,
};

Sonra şu şekilde başlayın:

let x : IX = deepCopy(XDef);

Tüm gereken bu ..

.. ancak ..

Herhangi bir kök öğesini özel olarak başlatmak istiyorsanız, deepCopy işlevini özel varsayılan değerleri kabul edecek şekilde değiştirebilirsiniz. İşlev şu hale gelir:

deepCopyAssign = <T extends {}>(input: any, rootOverwrites?: any): T => {
  return JSON.parse(JSON.stringify({ ...input, ...rootOverwrites }));
};

Daha sonra bunun yerine şu şekilde çağrılabilir:

let x : IX = deepCopyAssign(XDef, { a:'customInitValue' } );

Tercih edilen başka herhangi bir derin kopyalama yöntemi işe yarayacaktır. Yalnızca basit bir kopya gerekiyorsa, yardımcı program deepCopyveya deepCopyAssignişleve olan ihtiyacı ortadan kaldıran Object.assign yeterli olacaktır .

let x : IX = object.assign({}, XDef, { a:'customInitValue' });

Bilinen Sorunlar

  • Bu kılıkta derin bir görev vermeyecek, ancak değiştirmek çok zor değil deepCopyAssign atamadan önce türleri yinelemek ve kontrol etmek .
  • İşlevler ve referanslar ayrıştırma / dizgi işlemi tarafından kaybedilecektir. Görevim için bunlara ihtiyacım yok ve OP de yoktu.
  • Özel init değerleri, yürütüldüğünde kontrol edilen IDE veya tür tarafından ima edilmez.

0

Çözümüm:

Yazma sorunlarını düzeltmek için Object.assign üzerinde sarmalayıcı oluşturdum.

export function assign<T>(...args: T[] | Partial<T>[]): T {
  return Object.assign.apply(Object, [{}, ...args]);
}

Kullanımı:

env.base.ts

export interface EnvironmentValues {
export interface EnvironmentValues {
  isBrowser: boolean;
  apiURL: string;
}

export const enviromentBaseValues: Partial<EnvironmentValues> = {
  isBrowser: typeof window !== 'undefined',
};

export default enviromentBaseValues;

env.dev.ts

import { EnvironmentValues, enviromentBaseValues } from './env.base';
import { assign } from '../utilities';

export const enviromentDevValues: EnvironmentValues = assign<EnvironmentValues>(
  {
    apiURL: '/api',
  },
  enviromentBaseValues
);

export default enviromentDevValues;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.