Is it a bad idea to return different data types from a single function in a dynamically typed language?

My primary language is statically typed (Java). In Java, you have to return a single type from every method. For example, you can’t have a method that conditionally returns a String or conditionally returns an Integer. But in JavaScript, for example, this is very possible.

In a statically typed language I get why this is a bad idea. If every method returned Object (the common parent all classes inherit from) then you and the compiler have no idea what you’re dealing with. You’ll have to discover all your mistakes at run time.

But in a dynamically typed language, there may not even be a compiler. In a dynamically typed language, it’s not obvious to me why a function that returns multiple types is a bad idea. My background in static languages makes me avoid writing such functions, but I fear I’m being close minded about a feature that could make my code cleaner in ways I can’t see.


Edit: I’m going to remove my example (until I can think of a better one). I think it’s steering people to reply to a point I am not trying to make.

10

By contrast to other answers, there are cases where returning different types is acceptable.

Example 1

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>sum(2, 3) → int
sum(2.1, 3.7) → float
</code>
<code>sum(2, 3) → int sum(2.1, 3.7) → float </code>
sum(2, 3) → int
sum(2.1, 3.7) → float

In some statically typed languages, this involves overloads, so we can consider that there several methods, each one returning the predefined, fixed type. In dynamic languages, this may be the same function, implemented as:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>var sum = function (a, b) {
return a + b;
};
</code>
<code>var sum = function (a, b) { return a + b; }; </code>
var sum = function (a, b) {
    return a + b;
};

Same function, different types of return value.

Example 2

Imagine you get a response from an OpenID/OAuth component. Some OpenID/OAuth providers may contain more information, such as the age of the person.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>var user = authProvider.findCurrent();
// user is now:
// {
// provider: 'Facebook',
// name: {
// firstName: 'Hello',
// secondName: 'World',
// },
// email: '[email protected]',
// age: 27
// }
</code>
<code>var user = authProvider.findCurrent(); // user is now: // { // provider: 'Facebook', // name: { // firstName: 'Hello', // secondName: 'World', // }, // email: '[email protected]', // age: 27 // } </code>
var user = authProvider.findCurrent();
// user is now:
// {
//     provider: 'Facebook',
//     name: {
//         firstName: 'Hello',
//         secondName: 'World',
//     },
//     email: '[email protected]',
//     age: 27
// }

Others would have the minimum, would it be an email address or the pseudonym.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>var user = authProvider.findCurrent();
// user is now:
// {
// provider: 'Google',
// email: '[email protected]'
// }
</code>
<code>var user = authProvider.findCurrent(); // user is now: // { // provider: 'Google', // email: '[email protected]' // } </code>
var user = authProvider.findCurrent();
// user is now:
// {
//     provider: 'Google',
//     email: '[email protected]'
// }

Again, same function, different results.

Here, the benefit of returning different types is especially important in a context where you don’t care about types and interfaces, but what objects actually contain. For example, let’s imagine a website contains mature language. Then the findCurrent() may be used like this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>var user = authProvider.findCurrent();
if (user.age || 0 >= 16) {
// The person can stand mature language.
allowShowingContent();
} else if (user.age) {
// OpenID/OAuth gave the age, but the person appears too young to see the content.
showParentalAdvisoryRequestedMessage();
} else {
// OpenID/OAuth won't tell the age of the person. Ask the user himself.
askForAge();
}
</code>
<code>var user = authProvider.findCurrent(); if (user.age || 0 >= 16) { // The person can stand mature language. allowShowingContent(); } else if (user.age) { // OpenID/OAuth gave the age, but the person appears too young to see the content. showParentalAdvisoryRequestedMessage(); } else { // OpenID/OAuth won't tell the age of the person. Ask the user himself. askForAge(); } </code>
var user = authProvider.findCurrent();
if (user.age || 0 >= 16) {
    // The person can stand mature language.
    allowShowingContent();
} else if (user.age) {
    // OpenID/OAuth gave the age, but the person appears too young to see the content.
    showParentalAdvisoryRequestedMessage();
} else {
    // OpenID/OAuth won't tell the age of the person. Ask the user himself.
    askForAge();
}

Refactoring this into code where every provider will have its own function which will return a well-defined, fixed type would not only degrade the code base and cause code duplication, but also will not bring any benefit. One may end up doing horrors like:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>var age;
if (['Facebook', 'Yahoo', 'Blogger', 'LiveJournal'].contains(user.provider)) {
age = user.age;
}
</code>
<code>var age; if (['Facebook', 'Yahoo', 'Blogger', 'LiveJournal'].contains(user.provider)) { age = user.age; } </code>
var age;
if (['Facebook', 'Yahoo', 'Blogger', 'LiveJournal'].contains(user.provider)) {
    age = user.age;
}

15

In general, it’s a bad idea for the same reasons the moral equivalent in a statically typed language is a bad idea: You have no idea which concrete type is returned, so you have no idea what you can do with the result (aside from the few things that can be done with any value). In a static type system, you have compiler-checked annotations of return types and such, but in a dynamic language the same knowledge still exists – it’s just informal and stored in brains and documentation rather than in source code.

In many cases, however, there is rhyme and reason to which type is returned and the effect is similar to overloading or parametric polymorphism in static type systems. In other words, the result type is predictable, it’s just not quite as simple to express.

But note that there may be other reasons a specific function is badly designed: For example, a sum function returning false on invalid inputs is a bad idea primarily because that return value is useless and error-prone (0 <-> false confusion).

10

In dynamic languages, you shouldn’t ask whether return different types but objects with different APIs. As most dynamic languages don’t really care about types, but use various versions of duck typing.

When returning different types make sense

For example this method makes sense:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>def load_file(file):
if something:
return ['a ', 'list', 'of', 'strings']
return open(file, 'r')
</code>
<code>def load_file(file): if something: return ['a ', 'list', 'of', 'strings'] return open(file, 'r') </code>
def load_file(file): 
    if something: 
       return ['a ', 'list', 'of', 'strings'] 
    return open(file, 'r')

Because both file, and a list of strings are (in Python) iterables that return string. Very different types, the same API (unless someone tries to call file methods on a list, but this is different story).

You can conditionally return list or tuple (tuple is an immutable list in python).

Formally even doing:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>def do_something():
if ...:
return None
return something_else
</code>
<code>def do_something(): if ...: return None return something_else </code>
def do_something():
    if ...: 
        return None
    return something_else

or:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>function do_something(){
if (...) return null;
return sth;
}
</code>
<code>function do_something(){ if (...) return null; return sth; } </code>
function do_something(){
   if (...) return null; 
   return sth;
}

returns different types, as both python None and Javascript null are types on their own right.

All these use cases would have their counterpart in static languages, function would just return a proper interface.

When returning objects with different API conditionally is a good idea

As to whether returning different APIs is a good idea, IMO in most cases it doesn’t make sense. Only sensible example that comes to mind is something close to what @MainMa said: when your API can provide varying amount of detail it might make sense to return more detail when available.

7

Your question makes me want to cry a little. Not for the example usage you provided, but because someone will unwittingly take this approach too far. It’s a short step away from ridiculously unmaintainable code.

The error condition use case kind of makes sense, and the null pattern (everything has to be a pattern) in statically typed languages does the same type of thing. Your function call returns an object or it returns null.

But it’s a short step to saying “I’m going to use this to create a factory pattern” and return either foo or bar or baz depending upon the function’s mood. Debugging this will become a nightmare when the caller expects foo but was given bar.

So I don’t think you’re being closed minded. You’re being appropriately cautious in how use the language’s features.

Disclosure: my background is in statically typed languages, and I have generally worked on larger, diverse teams where the need for maintainable code was fairly high. So my perspective is probably skewed as well.

Using Generics in Java allows you to return a different type, while still retaining static type safety. You simply specify the type that you want to return in the generic type parameter of the function call.

Whether you could use a similar approach in Javascript is, of course, an open question. Since Javascript is a dynamically typed language, returning an object seems like the obvious choice.

If you want to know where a dynamic return scenario might work when you are used to working in statically-typed language, consider looking at the dynamic keyword in C#. Rob Conery was able to successfully write an Object-Relational Mapper in 400 lines of code using the dynamic keyword.

Of course, all dynamic really does is wrap an object variable with some runtime type safety.

It’s a bad idea, I think, to return different types conditionally. One of the ways this comes up frequently for me is if the function can return one or more values. If only one value needs to be returned, it may seem reasonable to just return the value, rather than packing it in an Array, to avoid having to unpack it in the calling function. However, this (and most other instances of this) places an obligation on the caller to distinguish between and handle both types. The function will be easier to reason about if it’s always returning the same type.

The “bad practice” exists regardless of whether your language is statically typed. The static language does more to steer you away from these practices, and you might find more users who complain about “bad practice” in a static language because it’s a more formal language. However, the underlying problems are there in a dynamic language, and you can determine whether they’re justified.

Here is the objectionable part of what you propose. If I don’t know what type is returned, then I can’t use the return value immediately. I have to “discover” something about it.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>total = sum_of_array([20, 30, 'q', 50])
if (type_of(total) == Boolean) {
display_error(...)
} else {
record_number(total)
}
</code>
<code>total = sum_of_array([20, 30, 'q', 50]) if (type_of(total) == Boolean) { display_error(...) } else { record_number(total) } </code>
total = sum_of_array([20, 30, 'q', 50])
if (type_of(total) == Boolean) {
  display_error(...)
} else {
  record_number(total)
}

Often this kind of switch in code is simply bad practice. It makes the code harder to read. In this example, you see why throwing and catching exception cases is popular. Put another way: if your function cannot do what it says it does, it should not return successfully. If I call your function, I want to do this:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>total = sum_of_array([20, 30, 'q', 50])
display_number(total)
</code>
<code>total = sum_of_array([20, 30, 'q', 50]) display_number(total) </code>
total = sum_of_array([20, 30, 'q', 50])
display_number(total)

Because the first line returns successfully, I assume that total actually contains the sum of the array. If it doesn’t return successfully, we jump to some other page of my program.

Let’s use another example that’s not just about propagating errors. Maybe sum_of_array tries to be smart and return a human-readable string in some cases, like “That’s my locker combination!” if and only if the array is [11,7,19]. I’m having trouble thinking of a good example. Anyway, the same problem applies. You have to inspect the return value before you can do anything with it:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>total = sum_of_array([20, 30, 40, 50])
if (type_of(total) == String) {
write_message(total)
} else {
record_number(total)
}
</code>
<code>total = sum_of_array([20, 30, 40, 50]) if (type_of(total) == String) { write_message(total) } else { record_number(total) } </code>
total = sum_of_array([20, 30, 40, 50])
if (type_of(total) == String) {
  write_message(total)
} else {
  record_number(total)
}

You might argue that it would be useful for the function to return an integer or a float, e.g.:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>sum_of_array(20, 30, 40) -> int
sum_of_array(23.45, 45.67, 67.789044) -> float
</code>
<code>sum_of_array(20, 30, 40) -> int sum_of_array(23.45, 45.67, 67.789044) -> float </code>
sum_of_array(20, 30, 40) -> int
sum_of_array(23.45, 45.67, 67.789044) -> float

But those results aren’t different types, as far as you’re concerned. You’re going to treat them both as numbers, and that’s all you care about. So sum_of_array returns the number type. This is what polymorphism is about.

So there are some practices you might be violating if your function can return multiple types. Knowing them will help you determine if your particular function should return multiple types anyway.

1

Actually, it’s not very uncommon at all to return different types even in a statically typed language. That’s why we have union types, for example.

In fact, methods in Java almost always return one of four types: some kind of object or null or an exception or they never return at all.

In many languages, error conditions are modeled as subroutines returning either a result type or an error type. For example, in Scala:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>def transferMoney(amount: Decimal): Either[String, Decimal]
</code>
<code>def transferMoney(amount: Decimal): Either[String, Decimal] </code>
def transferMoney(amount: Decimal): Either[String, Decimal]

This is a stupid example, of course. The return type means “either return a string or a decimal”. By convention, the left type is the error type (in this case, a string with the error message) and the right type is the result type.

This is similar to exceptions, except for the fact that exceptions also are control flow constructs. They are, in fact, equivalent in expressive power to GOTO.

4

No answer has yet mentioned SOLID principles. In particular, you should follow the Liskov substitution principle, that any class receiving a type other than the expected type can still work with whatever they get, without doing anything to test what type is returned.

So, if you throw some extra properties onto an object, or wrap a returned function with some kind of decorator that still accomplishes what the original function was intended to accomplish, you are good, as long as no code calling your function ever relies on this behavior, in any code path.

Instead of returning a string or an integer, a better example might be it returns a sprinkler system or a cat. This is fine if all the calling code is going to do is call functionInQuestion.hiss(). Effectively you have an implicit interface that the calling code is expecting, and a dynamically typed language won’t force you to make the interface explicit.

Sadly, your co-workers probably will, so you probably have to do the same work anyway in your documentation, except there isn’t a universally accepted, terse, machine-analyzable way to do it – as there is when you define an interface in a language that has them.

The one place where I see myself sending different types is for invalid input or “poor mans exceptions” where the “exceptional” condition isn’t very exceptional. For example, from my repository of PHP utility functions this abridged example:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>function ensure_fields($consideration)
{
$args = func_get_args();
foreach ( $args as $a ) {
if ( !is_string($a) ) {
return NULL;
}
if ( !isset($consideration[$a]) || $consideration[$a]=='' ) {
return FALSE;
}
}
return TRUE;
}
</code>
<code>function ensure_fields($consideration) { $args = func_get_args(); foreach ( $args as $a ) { if ( !is_string($a) ) { return NULL; } if ( !isset($consideration[$a]) || $consideration[$a]=='' ) { return FALSE; } } return TRUE; } </code>
function ensure_fields($consideration)
{
        $args = func_get_args();
        foreach ( $args as $a ) {
                if ( !is_string($a) ) {
                        return NULL;
                }
                if ( !isset($consideration[$a]) || $consideration[$a]=='' ) {
                        return FALSE;
                }
        }

        return TRUE;
}

The function nominally returns a BOOLEAN, however it returns NULL on invalid input. Note that since PHP 5.3, all internal PHP functions behave this way as well. Additionally, some internal PHP functions return FALSE or INT on nominal input, see:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>strpos('Hello', 'e'); // Returns INT(1)
strpos('Hello', 'q'); // Returns BOOL(FALSE)
</code>
<code>strpos('Hello', 'e'); // Returns INT(1) strpos('Hello', 'q'); // Returns BOOL(FALSE) </code>
strpos('Hello', 'e');  // Returns INT(1)
strpos('Hello', 'q');  // Returns BOOL(FALSE)

8

Developing software is basically an art and a craft of managing complexity. You try to narrow down the system at the points you can afford to, and limit the options at other points. Function’s interface is contract which helps to manage complexity of the code by limiting a knowledge required to work with any piece of code. By returning different types you significantly expand the function’s interface by adding to it all the interfaces of different types you return AND adding unobvious rules as to which interface is returned.

I don’t think this is a bad idea! In contrast to this most common opinion, and as already pointed out by Robert Harvey, Statically typed languages like Java introduced Generics exactly for situations like the one you are asking. Actually Java try to keep (wherever possible) typesafety at compile time, and sometimes Generics avoid code duplication, why? Because you can write the same method or the same class that handle/return different types. I’ll make just a very brief example to show this idea:

Java 1.4

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public static Boolean getBoolean(String property){
return (Boolean) properties.getProperty(property);
}
public static Integer getInt(String property){
return (Integer) properties.getProperty(property);
}
</code>
<code>public static Boolean getBoolean(String property){ return (Boolean) properties.getProperty(property); } public static Integer getInt(String property){ return (Integer) properties.getProperty(property); } </code>
public static Boolean getBoolean(String property){
    return (Boolean) properties.getProperty(property);
}
public static Integer getInt(String property){
    return (Integer) properties.getProperty(property);
}

Java 1.5+

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>public static <T> getValue(String property, Class<T> clazz) throws WhateverCheckedException{
return clazz.getConstructor(String.class).newInstance(properties.getProperty(property));
}
//the call will be
Boolean b = getValue("useProxy",Boolean.class);
Integer b = getValue("proxyPort",Integer.class);
</code>
<code>public static <T> getValue(String property, Class<T> clazz) throws WhateverCheckedException{ return clazz.getConstructor(String.class).newInstance(properties.getProperty(property)); } //the call will be Boolean b = getValue("useProxy",Boolean.class); Integer b = getValue("proxyPort",Integer.class); </code>
public static <T> getValue(String property, Class<T> clazz) throws WhateverCheckedException{
    return clazz.getConstructor(String.class).newInstance(properties.getProperty(property));
}
//the call will be
Boolean b = getValue("useProxy",Boolean.class);
Integer b = getValue("proxyPort",Integer.class);

In a dynamic typed language, since you have no typesafety at compile time, you are absolutely free to write the same code that works for many types.
Since even in a statically typed language Generics were introduced to solve this problem, it’s clearly a hint that writing a function that returns different types in a dynamic language is not a bad idea.

7

Perl uses this a lot because what a function does depends on its context. For instance, a function could return an array if used in list context, or the length of the array if used in a place where a scalar value is expected. From the tutorial that is the first hit for “perl context”, if you do:

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>my @now = localtime();
</code>
<code>my @now = localtime(); </code>
my @now = localtime();

Then @now is an array variable (that’s what the @ means), and will contain an array like (40, 51, 20, 9, 0, 109, 5, 8, 0).

If instead you call the function in a way where its result will have to be a scalar, with ($ variables are scalars):

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>my $now = localtime();
</code>
<code>my $now = localtime(); </code>
my $now = localtime();

then it does something completely different: $now will be something like “Fri Jan 9 20:51:40 2009”.

Another example I can think of is in implementing REST APIs, where the format of what is returned depends on what the client wants. E.g, HTML, or JSON, or XML. Although technically those are all streams of bytes, the idea is similar.

3

In dynamic-land it’s all about duck-typing. The most responsible exposed/public-facing thing to do is to wrap potentially different types in a wrapper that gives them the same interface.

Plain text
Copy to clipboard
Open code in new window
EnlighterJS 3 Syntax Highlighter
<code>function ThingyWrapper(thingy){ //a function constructor (class-like thingy)
//thingy is effectively private and persistent for ThingyWrapper instances
if(typeof thingy === 'array'){
this.alertItems = function(){
thingy.forEach(function(el){ alert(el); });
}
}
else {
this.alertItems = function(){
for(var x in thingy){ alert(thingy[x]); }
}
}
}
function gimmeThingy(){
var
coinToss = Math.round( Math.random() ),//gives me 0 or 1
arrayThingy = [1,2,3],
objectThingy = { item1:1, item2:2, item3:3 }
;
//0 dynamically evaluates to false in JS
return new ThingyWrapper( coinToss ? arrayThingy : objectThingy );
}
gimmeThingy().alertItems(); //should be same every time except order of numbers - maybe
</code>
<code>function ThingyWrapper(thingy){ //a function constructor (class-like thingy) //thingy is effectively private and persistent for ThingyWrapper instances if(typeof thingy === 'array'){ this.alertItems = function(){ thingy.forEach(function(el){ alert(el); }); } } else { this.alertItems = function(){ for(var x in thingy){ alert(thingy[x]); } } } } function gimmeThingy(){ var coinToss = Math.round( Math.random() ),//gives me 0 or 1 arrayThingy = [1,2,3], objectThingy = { item1:1, item2:2, item3:3 } ; //0 dynamically evaluates to false in JS return new ThingyWrapper( coinToss ? arrayThingy : objectThingy ); } gimmeThingy().alertItems(); //should be same every time except order of numbers - maybe </code>
function ThingyWrapper(thingy){ //a function constructor (class-like thingy)

    //thingy is effectively private and persistent for ThingyWrapper instances

    if(typeof thingy === 'array'){
        this.alertItems = function(){
            thingy.forEach(function(el){ alert(el); });
        }
    }
    else {
        this.alertItems = function(){
            for(var x in thingy){ alert(thingy[x]); }
        }
    }
}

function gimmeThingy(){
    var
        coinToss = Math.round( Math.random() ),//gives me 0 or 1
        arrayThingy = [1,2,3],
        objectThingy = { item1:1, item2:2, item3:3 }
    ;

    //0 dynamically evaluates to false in JS
    return new ThingyWrapper( coinToss ? arrayThingy : objectThingy );
}

gimmeThingy().alertItems(); //should be same every time except order of numbers - maybe

It might occasionally make sense to poop out different types altogether with no generic wrapper but honestly in 7ish years of writing JS, it’s not something I’ve found it was reasonable or convenient to do very often. Mostly that’s something I’d do in the context of closed environments like an object’s interior where things are getting put together. But it’s not something I’ve done often enough that any examples jump to mind.

Mostly, I would advise that you stop thinking about types as much altogether. You deal with types when you need to in a dynamic language. No more. It’s the whole point. Don’t check the type of every single argument. That’s something I would only be tempted to do in an environment where the same method could give inconsistent results in unobvious ways (so definitely don’t do something like that). But it’s not the type that matters, it’s how whatever it is that you give me works.

Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa Dịch vụ tổ chức sự kiện 5 sao Thông tin về chúng tôi Dịch vụ sinh nhật bé trai Dịch vụ sinh nhật bé gái Sự kiện trọn gói Các tiết mục giải trí Dịch vụ bổ trợ Tiệc cưới sang trọng Dịch vụ khai trương Tư vấn tổ chức sự kiện Hình ảnh sự kiện Cập nhật tin tức Liên hệ ngay Thuê chú hề chuyên nghiệp Tiệc tất niên cho công ty Trang trí tiệc cuối năm Tiệc tất niên độc đáo Sinh nhật bé Hải Đăng Sinh nhật đáng yêu bé Khánh Vân Sinh nhật sang trọng Bích Ngân Tiệc sinh nhật bé Thanh Trang Dịch vụ ông già Noel Xiếc thú vui nhộn Biểu diễn xiếc quay đĩa Dịch vụ tổ chức tiệc uy tín Khám phá dịch vụ của chúng tôi Tiệc sinh nhật cho bé trai Trang trí tiệc cho bé gái Gói sự kiện chuyên nghiệp Chương trình giải trí hấp dẫn Dịch vụ hỗ trợ sự kiện Trang trí tiệc cưới đẹp Khởi đầu thành công với khai trương Chuyên gia tư vấn sự kiện Xem ảnh các sự kiện đẹp Tin mới về sự kiện Kết nối với đội ngũ chuyên gia Chú hề vui nhộn cho tiệc sinh nhật Ý tưởng tiệc cuối năm Tất niên độc đáo Trang trí tiệc hiện đại Tổ chức sinh nhật cho Hải Đăng Sinh nhật độc quyền Khánh Vân Phong cách tiệc Bích Ngân Trang trí tiệc bé Thanh Trang Thuê dịch vụ ông già Noel chuyên nghiệp Xem xiếc khỉ đặc sắc Xiếc quay đĩa thú vị
Trang chủ Giới thiệu Sinh nhật bé trai Sinh nhật bé gái Tổ chức sự kiện Biểu diễn giải trí Dịch vụ khác Trang trí tiệc cưới Tổ chức khai trương Tư vấn dịch vụ Thư viện ảnh Tin tức - sự kiện Liên hệ Chú hề sinh nhật Trang trí YEAR END PARTY công ty Trang trí tất niên cuối năm Trang trí tất niên xu hướng mới nhất Trang trí sinh nhật bé trai Hải Đăng Trang trí sinh nhật bé Khánh Vân Trang trí sinh nhật Bích Ngân Trang trí sinh nhật bé Thanh Trang Thuê ông già Noel phát quà Biểu diễn xiếc khỉ Xiếc quay đĩa
Thiết kế website Thiết kế website Thiết kế website Cách kháng tài khoản quảng cáo Mua bán Fanpage Facebook Dịch vụ SEO Tổ chức sinh nhật